@beyondwork/docx-react-component 1.0.105 → 1.0.108

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.
Files changed (193) hide show
  1. package/package.json +19 -5
  2. package/src/api/geometry-overlay-rects.ts +5 -0
  3. package/src/api/package-version.ts +1 -1
  4. package/src/api/page-anchor-id.ts +5 -0
  5. package/src/api/public-types.ts +16 -9
  6. package/src/api/table-node-specs.ts +6 -0
  7. package/src/api/v3/_create.ts +10 -2
  8. package/src/api/v3/_page-anchor-id.ts +52 -0
  9. package/src/api/v3/_runtime-handle.ts +92 -1
  10. package/src/api/v3/ai/_audit-reference.ts +28 -0
  11. package/src/api/v3/ai/_audit-time.ts +5 -0
  12. package/src/api/v3/ai/_pe2-evidence.ts +310 -6
  13. package/src/api/v3/ai/attach.ts +29 -4
  14. package/src/api/v3/ai/bundle.ts +6 -2
  15. package/src/api/v3/ai/inspect.ts +6 -2
  16. package/src/api/v3/ai/replacement.ts +112 -18
  17. package/src/api/v3/ai/resolve.ts +2 -2
  18. package/src/api/v3/ai/review.ts +177 -3
  19. package/src/api/v3/index.ts +8 -0
  20. package/src/api/v3/runtime/collab.ts +462 -0
  21. package/src/api/v3/runtime/document.ts +503 -20
  22. package/src/api/v3/runtime/geometry.ts +97 -0
  23. package/src/api/v3/runtime/layout.ts +744 -0
  24. package/src/api/v3/runtime/perf-probe.ts +14 -0
  25. package/src/api/v3/runtime/viewport.ts +9 -8
  26. package/src/api/v3/ui/_types.ts +202 -55
  27. package/src/api/v3/ui/chrome-preset-model.ts +5 -5
  28. package/src/api/v3/ui/debug.ts +115 -2
  29. package/src/api/v3/ui/index.ts +17 -0
  30. package/src/api/v3/ui/overlays.ts +0 -8
  31. package/src/api/v3/ui/surface.ts +56 -0
  32. package/src/api/v3/ui/viewport.ts +119 -9
  33. package/src/core/commands/image-commands.ts +1 -0
  34. package/src/core/commands/index.ts +6 -0
  35. package/src/core/schema/text-schema.ts +43 -5
  36. package/src/core/selection/mapping.ts +8 -1
  37. package/src/core/selection/review-anchors.ts +5 -1
  38. package/src/core/state/text-transaction.ts +8 -2
  39. package/src/io/export/serialize-revisions.ts +149 -1
  40. package/src/io/normalize/normalize-text.ts +6 -0
  41. package/src/io/ooxml/parse-bookmark-references.ts +55 -0
  42. package/src/io/ooxml/parse-fields.ts +24 -2
  43. package/src/io/ooxml/parse-headers-footers.ts +38 -5
  44. package/src/io/ooxml/parse-main-document.ts +153 -9
  45. package/src/io/ooxml/parse-numbering.ts +20 -0
  46. package/src/io/ooxml/parse-revisions.ts +19 -8
  47. package/src/io/opc/package-reader.ts +98 -8
  48. package/src/model/anchor.ts +4 -3
  49. package/src/model/canonical-document.ts +220 -2
  50. package/src/model/canonical-hash.ts +221 -0
  51. package/src/model/canonical-layout-inputs.ts +245 -6
  52. package/src/model/layout/index.ts +1 -0
  53. package/src/model/layout/page-graph-types.ts +147 -1
  54. package/src/model/review/revision-types.ts +14 -3
  55. package/src/preservation/store.ts +20 -4
  56. package/src/review/README.md +1 -1
  57. package/src/review/store/revision-actions.ts +14 -2
  58. package/src/runtime/collab/event-types.ts +67 -1
  59. package/src/runtime/collab/runtime-collab-sync.ts +177 -5
  60. package/src/runtime/diagnostics/layout-guard-warning.ts +18 -0
  61. package/src/runtime/document-heading-outline.ts +147 -0
  62. package/src/runtime/document-navigation.ts +8 -243
  63. package/src/runtime/document-runtime.ts +279 -115
  64. package/src/runtime/edit-dispatch/dispatch-text-command.ts +11 -0
  65. package/src/runtime/formatting/layout-inputs.ts +38 -5
  66. package/src/runtime/formatting/numbering/geometry.ts +28 -2
  67. package/src/runtime/geometry/adjacent-geometry-intake.ts +835 -0
  68. package/src/runtime/geometry/caret-geometry.ts +5 -6
  69. package/src/runtime/geometry/geometry-facet.ts +60 -10
  70. package/src/runtime/geometry/geometry-index.ts +661 -16
  71. package/src/runtime/geometry/geometry-types.ts +59 -0
  72. package/src/runtime/geometry/hit-test.ts +11 -1
  73. package/src/runtime/geometry/overlay-rects.ts +5 -3
  74. package/src/runtime/geometry/project-anchors.ts +1 -1
  75. package/src/runtime/geometry/word-layout-v2-line-intake.ts +323 -0
  76. package/src/runtime/layout/index.ts +6 -0
  77. package/src/runtime/layout/layout-engine-instance.ts +6 -1
  78. package/src/runtime/layout/layout-engine-version.ts +188 -16
  79. package/src/runtime/layout/layout-facet-types.ts +6 -0
  80. package/src/runtime/layout/page-graph.ts +23 -4
  81. package/src/runtime/layout/paginated-layout-engine.ts +149 -15
  82. package/src/runtime/layout/project-block-fragments.ts +351 -14
  83. package/src/runtime/layout/public-facet.ts +162 -24
  84. package/src/runtime/layout/table-row-continuation-contract.ts +107 -0
  85. package/src/runtime/layout/table-row-split.ts +92 -35
  86. package/src/runtime/prerender/cache-envelope.ts +2 -2
  87. package/src/runtime/prerender/cache-key.ts +5 -4
  88. package/src/runtime/prerender/customxml-cache.ts +0 -1
  89. package/src/runtime/render/render-kernel.ts +1 -1
  90. package/src/runtime/revision-runtime.ts +112 -10
  91. package/src/runtime/scopes/_scope-dependencies.ts +1 -0
  92. package/src/runtime/scopes/action-validation.ts +22 -2
  93. package/src/runtime/scopes/capabilities.ts +316 -0
  94. package/src/runtime/scopes/compile-scope-bundle.ts +14 -0
  95. package/src/runtime/scopes/compiler-service.ts +108 -4
  96. package/src/runtime/scopes/content-control-evidence.ts +79 -0
  97. package/src/runtime/scopes/create-issue.ts +5 -5
  98. package/src/runtime/scopes/evidence.ts +91 -0
  99. package/src/runtime/scopes/formatting/apply.ts +2 -0
  100. package/src/runtime/scopes/geometry-evidence.ts +130 -0
  101. package/src/runtime/scopes/index.ts +54 -0
  102. package/src/runtime/scopes/issue-lifecycle.ts +224 -0
  103. package/src/runtime/scopes/layout-evidence.ts +374 -0
  104. package/src/runtime/scopes/multi-paragraph-refusal.ts +37 -0
  105. package/src/runtime/scopes/preservation-boundary.ts +7 -1
  106. package/src/runtime/scopes/replacement/apply.ts +97 -34
  107. package/src/runtime/scopes/scope-kinds/paragraph.ts +108 -12
  108. package/src/runtime/scopes/semantic-scope-types.ts +242 -3
  109. package/src/runtime/scopes/visualization.ts +28 -0
  110. package/src/runtime/surface-projection.ts +44 -5
  111. package/src/runtime/telemetry/perf-probe.ts +216 -0
  112. package/src/runtime/virtualized-rendering.ts +36 -1
  113. package/src/runtime/workflow/ai-issue-lifecycle.ts +253 -0
  114. package/src/runtime/workflow/coordinator.ts +39 -11
  115. package/src/runtime/workflow/derived-scope-resolver.ts +63 -9
  116. package/src/runtime/workflow/index.ts +4 -0
  117. package/src/runtime/workflow/overlay-lane-types.ts +58 -0
  118. package/src/runtime/workflow/overlay-lanes.ts +386 -0
  119. package/src/runtime/workflow/overlay-store.ts +2 -2
  120. package/src/runtime/workflow/redline-posture-calibration.ts +257 -0
  121. package/src/runtime/workflow/word-field-matrix-calibration.ts +231 -0
  122. package/src/session/_sync-legacy.ts +17 -27
  123. package/src/session/import/loader.ts +6 -4
  124. package/src/session/import/source-package-evidence.ts +186 -2
  125. package/src/session/index.ts +5 -6
  126. package/src/session/session.ts +30 -56
  127. package/src/session/types.ts +8 -13
  128. package/src/shell/session-bootstrap.ts +155 -81
  129. package/src/ui/WordReviewEditor.tsx +520 -12
  130. package/src/ui/editor-shell-view.tsx +14 -4
  131. package/src/ui/editor-surface-controller.tsx +5 -3
  132. package/src/ui/headless/selection-tool-resolver.ts +1 -2
  133. package/src/ui/presence-overlay-lane.ts +130 -0
  134. package/src/ui/ui-controller-factory.ts +17 -0
  135. package/src/ui-tailwind/chrome/build-context-menu-entries.ts +5 -1
  136. package/src/ui-tailwind/chrome/editor-action-registry.ts +105 -5
  137. package/src/ui-tailwind/chrome/editor-actions-to-palette.ts +7 -0
  138. package/src/ui-tailwind/chrome/layer-debug-contracts.ts +208 -0
  139. package/src/ui-tailwind/chrome/resolve-target-kind.ts +13 -0
  140. package/src/ui-tailwind/chrome/tw-alert-banner.tsx +11 -3
  141. package/src/ui-tailwind/chrome/tw-command-palette.tsx +36 -6
  142. package/src/ui-tailwind/chrome/tw-context-menu.tsx +6 -1
  143. package/src/ui-tailwind/chrome/tw-display-mode-selector.tsx +42 -109
  144. package/src/ui-tailwind/chrome/tw-inline-find-bar.tsx +26 -6
  145. package/src/ui-tailwind/chrome/tw-navigation-command-bar.tsx +328 -0
  146. package/src/ui-tailwind/chrome/tw-object-context-toolbar.tsx +8 -4
  147. package/src/ui-tailwind/chrome/tw-runtime-repl-dialog.tsx +129 -1
  148. package/src/ui-tailwind/chrome/tw-selection-tool-host.tsx +19 -5
  149. package/src/ui-tailwind/chrome/tw-selection-tool-structure.tsx +5 -1
  150. package/src/ui-tailwind/chrome/tw-workspace-chrome-host.tsx +28 -12
  151. package/src/ui-tailwind/chrome-overlay/tw-chrome-overlay.tsx +30 -3
  152. package/src/ui-tailwind/chrome-overlay/tw-object-selection-overlay.tsx +116 -10
  153. package/src/ui-tailwind/chrome-overlay/tw-page-stack-overlay-layer.tsx +223 -94
  154. package/src/ui-tailwind/chrome-overlay/tw-presence-overlay-lane.tsx +157 -0
  155. package/src/ui-tailwind/chrome-overlay/tw-review-overlay-lane-markers.tsx +259 -0
  156. package/src/ui-tailwind/chrome-overlay/tw-scope-card-layer.tsx +5 -2
  157. package/src/ui-tailwind/chrome-overlay/tw-substrate-overlay-lanes.tsx +314 -0
  158. package/src/ui-tailwind/debug/README.md +4 -1
  159. package/src/ui-tailwind/debug/layer11-consumer-readiness.ts +272 -0
  160. package/src/ui-tailwind/debug/layer11-word-field-matrix-evidence.ts +160 -0
  161. package/src/ui-tailwind/editor-surface/perf-probe.ts +14 -215
  162. package/src/ui-tailwind/editor-surface/pm-decorations.ts +42 -0
  163. package/src/ui-tailwind/editor-surface/pm-position-map.ts +38 -2
  164. package/src/ui-tailwind/editor-surface/pm-schema.ts +14 -4
  165. package/src/ui-tailwind/editor-surface/pm-state-from-snapshot.ts +34 -5
  166. package/src/ui-tailwind/editor-surface/runtime-decoration-plugin.ts +9 -19
  167. package/src/ui-tailwind/editor-surface/surface-build-keys.ts +2 -2
  168. package/src/ui-tailwind/editor-surface/tw-page-block-view.helpers.ts +145 -0
  169. package/src/ui-tailwind/editor-surface/tw-page-block-view.tsx +16 -11
  170. package/src/ui-tailwind/editor-surface/tw-prosemirror-surface.tsx +8 -10
  171. package/src/ui-tailwind/editor-surface/tw-table-node-view.tsx +3 -0
  172. package/src/ui-tailwind/page-stack/tw-page-chrome-entry.tsx +4 -2
  173. package/src/ui-tailwind/page-stack/tw-page-stack-chrome-layer.tsx +60 -20
  174. package/src/ui-tailwind/page-stack/tw-region-block-renderer.tsx +16 -11
  175. package/src/ui-tailwind/review/tw-health-panel.tsx +36 -17
  176. package/src/ui-tailwind/review/tw-review-rail.tsx +7 -4
  177. package/src/ui-tailwind/review-workspace/diagnostics-visibility.ts +44 -0
  178. package/src/ui-tailwind/review-workspace/page-shell-metrics.ts +11 -0
  179. package/src/ui-tailwind/review-workspace/tw-review-workspace-rail.tsx +16 -1
  180. package/src/ui-tailwind/review-workspace/types.ts +26 -12
  181. package/src/ui-tailwind/review-workspace/use-diagnostics-signal.ts +40 -11
  182. package/src/ui-tailwind/review-workspace/use-layout-facet-render-signal.ts +2 -1
  183. package/src/ui-tailwind/review-workspace/use-page-markers.ts +15 -26
  184. package/src/ui-tailwind/review-workspace/use-scope-card-state.ts +35 -18
  185. package/src/ui-tailwind/review-workspace/use-selection-toolbar-placement.ts +41 -32
  186. package/src/ui-tailwind/review-workspace/use-status-bar-page-facts.ts +2 -1
  187. package/src/ui-tailwind/review-workspace/use-workspace-side-effects.ts +2 -1
  188. package/src/ui-tailwind/status/tw-status-bar.tsx +6 -5
  189. package/src/ui-tailwind/toolbar/tw-role-action-region.tsx +52 -80
  190. package/src/ui-tailwind/toolbar/tw-shell-header.tsx +12 -48
  191. package/src/ui-tailwind/toolbar/tw-toolbar-icon-button.tsx +9 -4
  192. package/src/ui-tailwind/toolbar/tw-toolbar.tsx +328 -361
  193. package/src/ui-tailwind/tw-review-workspace.tsx +152 -286
@@ -6,15 +6,19 @@ import type {
6
6
  EditorError,
7
7
  EditorRole,
8
8
  FormattingStateSnapshot,
9
+ GeometryFacet,
9
10
  InteractionGuardSnapshot,
10
11
  ReviewQueueSnapshot,
11
12
  RuntimeContextAnalyticsSnapshot,
12
13
  RuntimeRenderSnapshot,
14
+ CollabSession,
13
15
  StyleCatalogSnapshot,
14
16
  TableStructureContextSnapshot,
17
+ WordReviewEditorLayoutFacet,
15
18
  WordReviewEditorChromeOptions,
16
19
  WordReviewEditorChromePreset,
17
20
  WordReviewEditorChromeVisibility,
21
+ WorkflowFacet,
18
22
  WorkflowScopeSnapshot,
19
23
  WorkspaceMode,
20
24
  ZoomLevel,
@@ -30,6 +34,10 @@ import type { ReviewRailTab } from "../ui-tailwind/review/tw-review-rail.tsx";
30
34
  import { TwReviewWorkspace } from "../ui-tailwind/tw-review-workspace.tsx";
31
35
  import type { EditorViewStateSnapshot } from "../api/public-types.ts";
32
36
  import type { MediaPreviewDescriptor } from "../ui-tailwind/editor-surface/pm-state-from-snapshot.ts";
37
+ import type {
38
+ UiObjectDragSession,
39
+ UiObjectDragStartInput,
40
+ } from "../api/v3/ui/_types.ts";
33
41
 
34
42
  export interface EditorShellViewProps {
35
43
  shellRef: React.RefObject<HTMLDivElement | null>;
@@ -64,7 +72,7 @@ export interface EditorShellViewProps {
64
72
  * ChromeOverlay (scope rail, workflow dock, etc.) can render over the
65
73
  * document column and the review rail's Workflow tab can read segments.
66
74
  */
67
- layoutFacet?: import("../runtime/layout/index.ts").WordReviewEditorLayoutFacet;
75
+ layoutFacet?: WordReviewEditorLayoutFacet;
68
76
  /**
69
77
  * Layer-05 geometry facet (`runtime.geometry`). Passed through to
70
78
  * `TwReviewWorkspace` which forwards it to the chrome overlay
@@ -73,7 +81,7 @@ export interface EditorShellViewProps {
73
81
  * `layoutFacet.getRenderFrame` / `.getRenderZoom` in refactor/05
74
82
  * cross-lane-coord §8.4.
75
83
  */
76
- geometryFacet?: import("../runtime/geometry/index.ts").GeometryFacet;
84
+ geometryFacet?: GeometryFacet;
77
85
  /**
78
86
  * Layer-06 workflow facet — canonical source of scope rail segments +
79
87
  * scope card models. Passed through to `TwReviewWorkspace` which
@@ -81,7 +89,7 @@ export interface EditorShellViewProps {
81
89
  * Slice 4 made this the owning seam; the layout facet no longer
82
90
  * exposes rail/card data.
83
91
  */
84
- workflowFacet?: import("../runtime/workflow/rail/types.ts").WorkflowFacet;
92
+ workflowFacet?: WorkflowFacet;
85
93
  interactionGuardSnapshot?: InteractionGuardSnapshot;
86
94
  chromePreset?: WordReviewEditorChromePreset;
87
95
  chromeOptions?: Partial<WordReviewEditorChromeOptions>;
@@ -94,7 +102,7 @@ export interface EditorShellViewProps {
94
102
  inlineFindBar?: ReactNode;
95
103
  onOpenInlineFind?: () => void;
96
104
  /** P9g — live collab session for the `"collab"` chrome preset's top nav. */
97
- collabSession?: import("../runtime/collab-session.ts").CollabSession;
105
+ collabSession?: CollabSession;
98
106
  collabTransportStatus?: import("../api/awareness-identity-types.ts").TransportStatus;
99
107
  collabActorId?: string;
100
108
  collabSendBaseline?: {
@@ -168,6 +176,8 @@ export interface EditorShellViewProps {
168
176
  }) => void;
169
177
  /** N6 — deselects the currently grabbed object; wired to runtime.deselectObject(). */
170
178
  onDeselectObject?: () => void;
179
+ /** Object handle drag lifecycle; routed through the mounted UI API seam. */
180
+ onBeginObjectDrag?: (input: UiObjectDragStartInput) => UiObjectDragSession;
171
181
  mediaPreviews?: Record<string, MediaPreviewDescriptor>;
172
182
  onActivateFloatingImage?: (payload: {
173
183
  mediaId: string;
@@ -4,10 +4,12 @@ import type {
4
4
  DocumentNavigationSnapshot,
5
5
  EditorStoryTarget,
6
6
  EditorUser,
7
+ GeometryFacet,
7
8
  RuntimeRenderSnapshot,
8
9
  SearchOptions,
9
10
  SearchResultSnapshot,
10
11
  SelectionSnapshot,
12
+ WordReviewEditorLayoutFacet,
11
13
  WorkflowBlockedCommandReason,
12
14
  WorkflowCandidateRange,
13
15
  WorkflowLockedZone,
@@ -33,7 +35,7 @@ export interface EditorSurfaceControllerProps {
33
35
  documentNavigation: DocumentNavigationSnapshot;
34
36
  reviewMode: "editing" | "review";
35
37
  markupDisplay: MarkupDisplay;
36
- showUnsupportedObjectPreviews?: boolean;
38
+ unsupportedObjectPreviewsVisible?: boolean;
37
39
  activeRevisionId?: string;
38
40
  activeSelectionToolKind?: ActiveSelectionToolModel["kind"] | null;
39
41
  showTrackedChanges?: boolean;
@@ -113,12 +115,12 @@ export interface EditorSurfaceControllerProps {
113
115
  dispatchRuntimeCommand?: (
114
116
  command: import("../ui-tailwind/editor-surface/fast-text-edit-lane.ts").LaneRuntimeCommand,
115
117
  ) => import("../api/public-types.ts").TextCommandAck;
116
- layoutFacet?: import("../runtime/layout/index.ts").WordReviewEditorLayoutFacet;
118
+ layoutFacet?: WordReviewEditorLayoutFacet;
117
119
  /**
118
120
  * Geometry facet forwarded to `TwProseMirrorSurface` for page-break
119
121
  * decoration build path (refactor/05 cross-lane-coord §8.4).
120
122
  */
121
- geometryFacet?: import("../runtime/geometry/index.ts").GeometryFacet;
123
+ geometryFacet?: GeometryFacet;
122
124
  pageChromeHeaderBandPx?: number;
123
125
  pageChromeFooterBandPx?: number;
124
126
  pageChromeInterGapPx?: number;
@@ -376,8 +376,7 @@ export function buildStructureContextSelectionToolModel(
376
376
  ),
377
377
  );
378
378
  const isTableTextRangeContext =
379
- input.activeTableContext?.selectionKind === "text" &&
380
- !input.snapshot.selection.isCollapsed;
379
+ input.activeTableContext?.selectionKind === "text";
381
380
  if (isTableTextRangeContext) {
382
381
  return null;
383
382
  }
@@ -0,0 +1,130 @@
1
+ import {
2
+ getRemoteCursorStates,
3
+ type RemoteCursorState,
4
+ } from "../api/v3/runtime/collab.ts";
5
+ import type {
6
+ GeometryRect,
7
+ UiListener,
8
+ UiOverlayLaneKind,
9
+ UiOverlayLaneSnapshot,
10
+ UiOverlayLaneStatus,
11
+ UiUnsubscribe,
12
+ } from "../api/v3/ui/_types.ts";
13
+
14
+ type AwarenessChangeListener = () => void;
15
+
16
+ export interface PresenceAwarenessSource {
17
+ readonly clientID: number;
18
+ getStates(): Map<number, Record<string, unknown>>;
19
+ on(event: "change", listener: AwarenessChangeListener): void;
20
+ off(event: "change", listener: AwarenessChangeListener): void;
21
+ }
22
+
23
+ export type PresenceCursorProjection =
24
+ | { readonly status: "resolved"; readonly rects: readonly GeometryRect[]; readonly reason?: string }
25
+ | { readonly status: "requires-rehydration" | "unavailable"; readonly rects?: undefined; readonly reason?: string };
26
+
27
+ export interface PresenceOverlayLaneSourceOptions {
28
+ readonly awareness: PresenceAwarenessSource;
29
+ readonly localClientId?: number;
30
+ readonly getRevision?: () => number;
31
+ readonly projectCursor?: (cursor: RemoteCursorState) => PresenceCursorProjection;
32
+ }
33
+
34
+ export interface PresenceOverlayLaneSource {
35
+ getLane(kind: UiOverlayLaneKind): UiOverlayLaneSnapshot;
36
+ subscribeLane(
37
+ kind: UiOverlayLaneKind,
38
+ listener: UiListener<UiOverlayLaneSnapshot>,
39
+ ): UiUnsubscribe;
40
+ }
41
+
42
+ function unavailable(kind: UiOverlayLaneKind, reason: string): UiOverlayLaneSnapshot {
43
+ return Object.freeze({
44
+ kind,
45
+ status: "unavailable",
46
+ entries: Object.freeze([]),
47
+ revision: 0,
48
+ source: "unavailable",
49
+ reason,
50
+ } as const);
51
+ }
52
+
53
+ function coerceLaneStatus(
54
+ current: UiOverlayLaneStatus,
55
+ next: UiOverlayLaneStatus,
56
+ ): UiOverlayLaneStatus {
57
+ if (current === "unavailable" || next === "unavailable") return "unavailable";
58
+ if (current === "requires-rehydration" || next === "requires-rehydration") {
59
+ return "requires-rehydration";
60
+ }
61
+ return "resolved";
62
+ }
63
+
64
+ export function createPresenceOverlayLaneSource(
65
+ options: PresenceOverlayLaneSourceOptions,
66
+ ): PresenceOverlayLaneSource {
67
+ const localClientId = options.localClientId ?? options.awareness.clientID;
68
+
69
+ function readPresenceLane(): UiOverlayLaneSnapshot {
70
+ const cursors = getRemoteCursorStates(
71
+ options.awareness as Parameters<typeof getRemoteCursorStates>[0],
72
+ localClientId,
73
+ );
74
+ let laneStatus: UiOverlayLaneStatus = "resolved";
75
+ const entries = cursors.map((cursor) => {
76
+ const projection = options.projectCursor?.(cursor) ?? {
77
+ status: "requires-rehydration" as const,
78
+ reason: "presence cursor geometry projection is not wired",
79
+ };
80
+ laneStatus = coerceLaneStatus(laneStatus, projection.status);
81
+ return {
82
+ id: `presence:${cursor.userId}`,
83
+ status: projection.status,
84
+ ...(projection.rects ? { rects: projection.rects } : {}),
85
+ ...(projection.reason ? { reason: projection.reason } : {}),
86
+ data: {
87
+ kind: "remote-cursor",
88
+ userId: cursor.userId,
89
+ displayName: cursor.displayName,
90
+ color: cursor.color,
91
+ anchor: cursor.anchor,
92
+ head: cursor.head,
93
+ storyTarget: cursor.storyTarget,
94
+ },
95
+ };
96
+ });
97
+
98
+ return {
99
+ kind: "presence",
100
+ status: entries.length === 0 ? "resolved" : laneStatus,
101
+ entries,
102
+ revision: options.getRevision?.() ?? 0,
103
+ source: "awareness",
104
+ };
105
+ }
106
+
107
+ return {
108
+ getLane(kind: UiOverlayLaneKind): UiOverlayLaneSnapshot {
109
+ if (kind !== "presence") {
110
+ return unavailable(kind, "presence lane source only handles kind='presence'");
111
+ }
112
+ return readPresenceLane();
113
+ },
114
+ subscribeLane(
115
+ kind: UiOverlayLaneKind,
116
+ listener: UiListener<UiOverlayLaneSnapshot>,
117
+ ): UiUnsubscribe {
118
+ if (kind !== "presence") {
119
+ throw new Error(`presence lane source only handles kind='presence'; got "${kind}"`);
120
+ }
121
+ const onChange = (): void => {
122
+ listener(readPresenceLane());
123
+ };
124
+ options.awareness.on("change", onChange);
125
+ return () => {
126
+ options.awareness.off("change", onChange);
127
+ };
128
+ },
129
+ };
130
+ }
@@ -54,11 +54,14 @@ import type {
54
54
  ChromePosture,
55
55
  GeometryRect,
56
56
  OverlayAnchorQuery,
57
+ PageResidencySnapshot,
57
58
  UiOverlayLaneKind,
58
59
  UiOverlayLaneSnapshot,
59
60
  UiController,
60
61
  UiControllerFactory,
61
62
  UiListener,
63
+ UiObjectDragSession,
64
+ UiObjectDragStartInput,
62
65
  UiUnsubscribe,
63
66
  ViewportState,
64
67
  } from "../api/v3/ui/_types.ts";
@@ -113,6 +116,13 @@ export interface ShellUiControllerDeps {
113
116
  * dpr / container-resize. Returns unsubscribe.
114
117
  */
115
118
  readonly subscribeViewport?: (listener: UiListener<ViewportState>) => UiUnsubscribe;
119
+ /** PE2 page-residency policy read. */
120
+ readonly getPageResidency?: (pageIndex: number) => PageResidencySnapshot;
121
+ /** PE2 page-residency subscription channel. */
122
+ readonly subscribePageResidency?: (
123
+ pageIndex: number,
124
+ listener: UiListener<PageResidencySnapshot>,
125
+ ) => UiUnsubscribe;
116
126
  /**
117
127
  * Overlay invalidation stream — fires when geometry invalidation overlaps
118
128
  * an attached overlay query (U7). Typically delegated to the render
@@ -128,6 +138,10 @@ export interface ShellUiControllerDeps {
128
138
  kind: UiOverlayLaneKind,
129
139
  listener: UiListener<UiOverlayLaneSnapshot>,
130
140
  ) => UiUnsubscribe;
141
+ /** Mounted object-handle drag lifecycle. */
142
+ readonly beginObjectDrag?: (
143
+ input: UiObjectDragStartInput,
144
+ ) => UiObjectDragSession;
131
145
  }
132
146
 
133
147
  /**
@@ -154,9 +168,12 @@ export function makeShellUiControllerFactory(
154
168
  ...(deps.subscribeChrome ? { subscribeChrome: deps.subscribeChrome } : {}),
155
169
  ...(deps.getViewport ? { getViewport: deps.getViewport } : {}),
156
170
  ...(deps.subscribeViewport ? { subscribeViewport: deps.subscribeViewport } : {}),
171
+ ...(deps.getPageResidency ? { getPageResidency: deps.getPageResidency } : {}),
172
+ ...(deps.subscribePageResidency ? { subscribePageResidency: deps.subscribePageResidency } : {}),
157
173
  ...(deps.subscribeOverlays ? { subscribeOverlays: deps.subscribeOverlays } : {}),
158
174
  ...(deps.getOverlayLane ? { getOverlayLane: deps.getOverlayLane } : {}),
159
175
  ...(deps.subscribeOverlayLane ? { subscribeOverlayLane: deps.subscribeOverlayLane } : {}),
176
+ ...(deps.beginObjectDrag ? { beginObjectDrag: deps.beginObjectDrag } : {}),
160
177
  };
161
178
  return controller;
162
179
  };
@@ -20,6 +20,8 @@ export interface BuildContextMenuInput {
20
20
  readonly targetKinds: readonly TargetKind[];
21
21
  readonly mode: EditorChromeMode;
22
22
  readonly host: EditorActionHostCallbacks;
23
+ /** Explicit operator/debug opt-in for opaque preservation diagnostics. */
24
+ readonly debugMode?: boolean;
23
25
  /**
24
26
  * Called when an action's row is selected. Typically closes the menu
25
27
  * and returns focus to the editor selection.
@@ -49,6 +51,7 @@ export function buildContextMenuEntries(
49
51
  const dispatchCtx: EditorActionDispatchContext = {
50
52
  composition: { mode: input.mode },
51
53
  host: input.host,
54
+ debugMode: input.debugMode === true,
52
55
  dismiss: input.dismiss,
53
56
  };
54
57
 
@@ -61,7 +64,7 @@ export function buildContextMenuEntries(
61
64
  targetKind,
62
65
  input.mode,
63
66
  input.host,
64
- { includeDisabled: true },
67
+ { includeDisabled: true, debugMode: input.debugMode === true },
65
68
  );
66
69
  for (const { action, disabled } of resolved) {
67
70
  if (seenActionIds.has(action.id)) continue;
@@ -77,6 +80,7 @@ export function buildContextMenuEntries(
77
80
  id: action.id,
78
81
  label: action.label,
79
82
  group: action.group,
83
+ ...(action.description ? { description: action.description } : {}),
80
84
  ...(action.shortcut ? { shortcut: [...action.shortcut] } : {}),
81
85
  ...(disabled ? { disabled: true } : {}),
82
86
  onSelect: () => action.run(dispatchCtx),
@@ -45,6 +45,7 @@ export type TargetKind =
45
45
  | "comment-anchor" // caret at a comment-anchored range
46
46
  | "suggestion" // caret at a tracked-change suggestion
47
47
  | "opaque-block" // preserved-for-export placeholder
48
+ | "template-slot" // SDT/content-control template slot
48
49
  | "scope-anchor"; // caret inside a workflow scope
49
50
 
50
51
  /**
@@ -55,6 +56,7 @@ export type TargetKind =
55
56
  export interface EditorActionDispatchContext {
56
57
  readonly composition: { readonly mode: EditorChromeMode };
57
58
  readonly host: EditorActionHostCallbacks;
59
+ readonly debugMode?: boolean;
58
60
  /** Close the menu / palette after dispatching, if applicable. */
59
61
  readonly dismiss: () => void;
60
62
  }
@@ -132,6 +134,9 @@ export interface EditorActionHostCallbacks {
132
134
 
133
135
  // Object operations (object target — read-only info by DESIGN-EDITOR.md §6.10)
134
136
  readonly onOpenObjectInfo?: () => void;
137
+ readonly onOpenPreservedContentInfo?: () => void;
138
+ readonly onOpenTemplateSlotInfo?: () => void;
139
+ readonly onOpenTrustDetails?: () => void;
135
140
 
136
141
  // Hyperlink operations (hyperlink target)
137
142
  readonly onEditHyperlink?: () => void;
@@ -142,6 +147,7 @@ export interface EditorActionHostCallbacks {
142
147
  readonly onJumpToScopeInRail?: () => void;
143
148
  readonly onOpenScopeCard?: () => void;
144
149
  readonly onMarkScopeResolved?: () => void;
150
+ readonly onAskAgentForSelection?: () => void;
145
151
  }
146
152
 
147
153
  export interface EditorAction {
@@ -157,6 +163,8 @@ export interface EditorAction {
157
163
  * Empty / undefined means "always in scope of its target".
158
164
  */
159
165
  readonly modes?: ReadonlySet<EditorChromeMode>;
166
+ /** Debug/operator-only rows never render in default product chrome. */
167
+ readonly debugOnly?: boolean;
160
168
  /**
161
169
  * Optional availability predicate. Return `false` to hide the row;
162
170
  * `true` to show; `"disabled"` to show as a non-interactive disabled
@@ -186,6 +194,7 @@ function mk<K extends keyof EditorActionHostCallbacks>(opts: {
186
194
  group: ContextMenuGroupId;
187
195
  targetKinds: readonly TargetKind[];
188
196
  modes?: readonly EditorChromeMode[];
197
+ debugOnly?: boolean;
189
198
  callback: K;
190
199
  /** Optional extra predicate run after the callback-presence check. */
191
200
  when?: (ctx: EditorActionDispatchContext) => boolean | "disabled";
@@ -198,7 +207,9 @@ function mk<K extends keyof EditorActionHostCallbacks>(opts: {
198
207
  group: opts.group,
199
208
  targetKinds: new Set(opts.targetKinds),
200
209
  ...(opts.modes ? { modes: new Set(opts.modes) } : {}),
210
+ ...(opts.debugOnly ? { debugOnly: opts.debugOnly } : {}),
201
211
  when: (ctx) => {
212
+ if (opts.debugOnly && ctx.debugMode !== true) return false;
202
213
  if (typeof ctx.host[opts.callback] !== "function") return false;
203
214
  if (opts.when) return opts.when(ctx);
204
215
  return true;
@@ -232,6 +243,7 @@ function mkArg<A, K extends keyof EditorActionHostCallbacks>(opts: {
232
243
  group: ContextMenuGroupId;
233
244
  targetKinds: readonly TargetKind[];
234
245
  modes?: readonly EditorChromeMode[];
246
+ debugOnly?: boolean;
235
247
  callback: K;
236
248
  payload: A;
237
249
  when?: (ctx: EditorActionDispatchContext) => boolean | "disabled";
@@ -244,7 +256,9 @@ function mkArg<A, K extends keyof EditorActionHostCallbacks>(opts: {
244
256
  group: opts.group,
245
257
  targetKinds: new Set(opts.targetKinds),
246
258
  ...(opts.modes ? { modes: new Set(opts.modes) } : {}),
259
+ ...(opts.debugOnly ? { debugOnly: opts.debugOnly } : {}),
247
260
  when: (ctx) => {
261
+ if (opts.debugOnly && ctx.debugMode !== true) return false;
248
262
  if (typeof ctx.host[opts.callback] !== "function") return false;
249
263
  if (opts.when) return opts.when(ctx);
250
264
  return true;
@@ -325,6 +339,7 @@ export const EDITOR_ACTION_REGISTRY: readonly EditorAction[] = [
325
339
  "suggestion",
326
340
  "comment-anchor",
327
341
  "opaque-block",
342
+ "template-slot",
328
343
  "scope-anchor",
329
344
  ],
330
345
  callback: "onCut",
@@ -357,6 +372,7 @@ export const EDITOR_ACTION_REGISTRY: readonly EditorAction[] = [
357
372
  "hyperlink",
358
373
  "suggestion",
359
374
  "comment-anchor",
375
+ "template-slot",
360
376
  "scope-anchor",
361
377
  ],
362
378
  callback: "onPaste",
@@ -402,6 +418,22 @@ export const EDITOR_ACTION_REGISTRY: readonly EditorAction[] = [
402
418
  callback: "onSetParagraphStyle",
403
419
  payload: "Normal",
404
420
  }),
421
+ mkArg<string, "onSetParagraphStyle">({
422
+ id: "style-title",
423
+ label: "Title",
424
+ group: "formatting",
425
+ targetKinds: ["plain-text", "table-cell"],
426
+ callback: "onSetParagraphStyle",
427
+ payload: "Title",
428
+ }),
429
+ mkArg<string, "onSetParagraphStyle">({
430
+ id: "style-quote",
431
+ label: "Quote",
432
+ group: "formatting",
433
+ targetKinds: ["plain-text", "table-cell"],
434
+ callback: "onSetParagraphStyle",
435
+ payload: "Quote",
436
+ }),
405
437
  mkArg<string, "onSetParagraphStyle">({
406
438
  id: "style-heading-1",
407
439
  label: "Heading 1",
@@ -570,6 +602,33 @@ export const EDITOR_ACTION_REGISTRY: readonly EditorAction[] = [
570
602
  callback: "onInsertSectionBreak",
571
603
  payload: "nextPage",
572
604
  }),
605
+ mkArg<ProductSectionBreakType, "onInsertSectionBreak">({
606
+ id: "insert-section-break-continuous",
607
+ label: "Insert continuous section break",
608
+ description: "Start a new section on the same page.",
609
+ group: "misc",
610
+ targetKinds: ["plain-text", "table-cell"],
611
+ callback: "onInsertSectionBreak",
612
+ payload: "continuous",
613
+ }),
614
+ mkArg<ProductSectionBreakType, "onInsertSectionBreak">({
615
+ id: "insert-section-break-even-page",
616
+ label: "Insert even-page section break",
617
+ description: "Start the new section on the next even page.",
618
+ group: "misc",
619
+ targetKinds: ["plain-text", "table-cell"],
620
+ callback: "onInsertSectionBreak",
621
+ payload: "evenPage",
622
+ }),
623
+ mkArg<ProductSectionBreakType, "onInsertSectionBreak">({
624
+ id: "insert-section-break-odd-page",
625
+ label: "Insert odd-page section break",
626
+ description: "Start the new section on the next odd page.",
627
+ group: "misc",
628
+ targetKinds: ["plain-text", "table-cell"],
629
+ callback: "onInsertSectionBreak",
630
+ payload: "oddPage",
631
+ }),
573
632
  mkImportant({
574
633
  id: "insert-image",
575
634
  label: "Insert image…",
@@ -608,7 +667,7 @@ export const EDITOR_ACTION_REGISTRY: readonly EditorAction[] = [
608
667
  mkImportant({
609
668
  id: "replace",
610
669
  label: "Replace…",
611
- description: "Host replace panel is not wired.",
670
+ description: "Find text and replace the active match.",
612
671
  shortcut: ["Ctrl", "H"],
613
672
  group: "misc",
614
673
  targetKinds: [],
@@ -617,7 +676,7 @@ export const EDITOR_ACTION_REGISTRY: readonly EditorAction[] = [
617
676
  mkImportant({
618
677
  id: "go-to",
619
678
  label: "Go to…",
620
- description: "Host navigation panel is not wired.",
679
+ description: "Jump to pages, headings, comments, changes, or scopes.",
621
680
  shortcut: ["Ctrl", "G"],
622
681
  group: "misc",
623
682
  targetKinds: [],
@@ -626,7 +685,7 @@ export const EDITOR_ACTION_REGISTRY: readonly EditorAction[] = [
626
685
  mkImportant({
627
686
  id: "print",
628
687
  label: "Print…",
629
- description: "Host print/export flow is not wired.",
688
+ description: "Open the print or export flow.",
630
689
  shortcut: ["Mod", "P"],
631
690
  group: "misc",
632
691
  targetKinds: [],
@@ -798,6 +857,32 @@ export const EDITOR_ACTION_REGISTRY: readonly EditorAction[] = [
798
857
  targetKinds: ["object"],
799
858
  callback: "onOpenObjectInfo",
800
859
  }),
860
+ mk({
861
+ id: "preserved-content-info",
862
+ label: "Preservation details",
863
+ description: "Show why this content is preserved and whether export remains safe.",
864
+ group: "misc",
865
+ targetKinds: ["object", "opaque-block"],
866
+ debugOnly: true,
867
+ callback: "onOpenPreservedContentInfo",
868
+ }),
869
+ mkImportant({
870
+ id: "template-slot-info",
871
+ label: "Template slot details",
872
+ description: "Show requirements, placeholder state, and scope targeting for this template slot.",
873
+ group: "misc",
874
+ targetKinds: ["template-slot"],
875
+ callback: "onOpenTemplateSlotInfo",
876
+ }),
877
+ mk({
878
+ id: "trust-details",
879
+ label: "Trust details",
880
+ description: "Open the health view for compatibility, preservation, and evidence status.",
881
+ group: "misc",
882
+ targetKinds: ["object", "opaque-block", "template-slot"],
883
+ debugOnly: true,
884
+ callback: "onOpenTrustDetails",
885
+ }),
801
886
 
802
887
  // -------- Hyperlink --------
803
888
  mk({
@@ -848,6 +933,15 @@ export const EDITOR_ACTION_REGISTRY: readonly EditorAction[] = [
848
933
  // Workflow-only — review posture inspects, doesn't mutate scope state.
849
934
  modes: ["workflow"],
850
935
  }),
936
+ mkImportant({
937
+ id: "ask-agent-selection",
938
+ label: "Ask agent about selection",
939
+ description: "Send this selection or scope to the host agent workflow.",
940
+ group: "misc",
941
+ targetKinds: ["plain-text", "table-cell", "scope-anchor", "template-slot"],
942
+ callback: "onAskAgentForSelection",
943
+ modes: ["review", "workflow"],
944
+ }),
851
945
  ];
852
946
 
853
947
  /**
@@ -861,6 +955,11 @@ export interface ResolvedEditorAction {
861
955
  readonly disabled: boolean;
862
956
  }
863
957
 
958
+ export interface ResolveActionsForTargetOptions {
959
+ includeDisabled?: boolean;
960
+ debugMode?: boolean;
961
+ }
962
+
864
963
  /**
865
964
  * Query the registry for every action whose target set includes `kind`
866
965
  * and whose mode filter (if any) matches the current composition mode.
@@ -881,17 +980,18 @@ export function resolveActionsForTarget(
881
980
  kind: TargetKind,
882
981
  mode: EditorChromeMode,
883
982
  host: EditorActionHostCallbacks,
884
- options: { includeDisabled: true },
983
+ options: ResolveActionsForTargetOptions & { includeDisabled: true },
885
984
  ): readonly ResolvedEditorAction[];
886
985
  export function resolveActionsForTarget(
887
986
  kind: TargetKind,
888
987
  mode: EditorChromeMode,
889
988
  host: EditorActionHostCallbacks,
890
- options?: { includeDisabled?: boolean },
989
+ options?: ResolveActionsForTargetOptions,
891
990
  ): readonly EditorAction[] | readonly ResolvedEditorAction[] {
892
991
  const ctx: EditorActionDispatchContext = {
893
992
  composition: { mode },
894
993
  host,
994
+ debugMode: options?.debugMode === true,
895
995
  dismiss: () => {},
896
996
  };
897
997
  const resolved: ResolvedEditorAction[] = [];
@@ -138,10 +138,14 @@ function paletteGroupForAction(action: EditorAction): ProductPaletteGroupId {
138
138
  case "jump-to-comment-in-rail":
139
139
  case "scope-jump-in-rail":
140
140
  case "scope-open-card":
141
+ case "template-slot-info":
141
142
  return "navigation";
142
143
  case "scope-mark-resolved":
144
+ case "ask-agent-selection":
143
145
  return "mode";
144
146
  case "object-info":
147
+ case "preserved-content-info":
148
+ case "trust-details":
145
149
  case "print":
146
150
  return "diagnostics";
147
151
  default:
@@ -152,6 +156,8 @@ function paletteGroupForAction(action: EditorAction): ProductPaletteGroupId {
152
156
  export interface BuildPaletteGroupsInput {
153
157
  readonly mode: EditorChromeMode;
154
158
  readonly host: EditorActionHostCallbacks;
159
+ /** Explicit operator/debug opt-in for opaque preservation diagnostics. */
160
+ readonly debugMode?: boolean;
155
161
  /** Called by every command's onInvoke — typically closes the palette. */
156
162
  readonly dismiss: () => void;
157
163
  }
@@ -162,6 +168,7 @@ export function buildPaletteGroupsFromRegistry(
162
168
  const ctx: EditorActionDispatchContext = {
163
169
  composition: { mode: input.mode },
164
170
  host: input.host,
171
+ debugMode: input.debugMode === true,
165
172
  dismiss: input.dismiss,
166
173
  };
167
174