@beyondwork/docx-react-component 1.0.91 → 1.0.93

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@beyondwork/docx-react-component",
3
3
  "publisher": "beyondwork",
4
- "version": "1.0.91",
4
+ "version": "1.0.93",
5
5
  "description": "Embeddable React Word (docx) editor with review, comments, tracked changes, and round-trip OOXML fidelity.",
6
6
  "type": "module",
7
7
  "sideEffects": [
@@ -25,8 +25,12 @@
25
25
 
26
26
  import type {
27
27
  OverlayKind,
28
+ ScopeCardModel,
28
29
  ScopeBundle,
30
+ ScopeRailSnapshot,
29
31
  SelectionSnapshot,
32
+ SemanticScope,
33
+ SemanticScopeKind,
30
34
  WorkflowMarkupMode,
31
35
  } from "../../public-types.ts";
32
36
  import type { GeometryRect } from "../../../runtime/geometry/geometry-types.ts";
@@ -549,11 +553,31 @@ export interface ApiV3UiDebug {
549
553
  detach(): void;
550
554
  }
551
555
 
556
+ export interface UiScopeListFilter {
557
+ readonly kind?: SemanticScopeKind;
558
+ readonly limit?: number;
559
+ }
560
+
561
+ export interface UiScopeRailOptions {
562
+ /**
563
+ * Narrow the snapshot to a single page index. Omit to span every page
564
+ * the runtime's page graph knows about.
565
+ */
566
+ readonly pageIndex?: number;
567
+ }
568
+
552
569
  /**
553
- * Scope seam — mounted-path scope-bundle reads. MVP surface unblocked
554
- * by L07's `compileScopeBundleById` handle primitive (2026-04-23).
570
+ * Scope seam — mounted-path scope reads. Uses handle-backed compiled
571
+ * projections so chrome surfaces do not reach directly into workflow
572
+ * facets for list, card, rail, or bundle data.
555
573
  */
556
574
  export interface ApiV3UiScope {
575
+ /** Enumerate compiled semantic scopes, optionally narrowed by kind/limit. */
576
+ list(filter?: UiScopeListFilter): readonly SemanticScope[];
577
+ /** Read the scope-card projection for a single scope id. */
578
+ card(scopeId: string): ScopeCardModel | null;
579
+ /** Read scope-rail segments across the document or a single page. */
580
+ rail(options?: UiScopeRailOptions): ScopeRailSnapshot;
557
581
  /**
558
582
  * Read the full `ScopeBundle` for a scope by id. Returns `null` when
559
583
  * the scope does not enumerate today (handle drift, synthetic scope,
@@ -21,6 +21,8 @@ export type {
21
21
  UiBinding,
22
22
  UiListener,
23
23
  UiUnsubscribe,
24
+ UiScopeListFilter,
25
+ UiScopeRailOptions,
24
26
  ViewportState,
25
27
  ScrollTarget,
26
28
  ScrollTargetBehavior,
@@ -138,6 +138,7 @@ import {
138
138
  type TwProseMirrorSurfaceRef,
139
139
  } from "../ui-tailwind/editor-surface/tw-prosemirror-surface";
140
140
  import { TwInlineFindBar } from "../ui-tailwind/chrome/tw-inline-find-bar";
141
+ import { PAGE_CHROME_DEFAULTS } from "../ui-tailwind/editor-surface/pm-page-break-decorations.ts";
141
142
  import type { MediaPreviewDescriptor } from "../ui-tailwind/editor-surface/pm-state-from-snapshot";
142
143
  import {
143
144
  incrementInvalidationCounter,
@@ -3753,7 +3754,7 @@ export const WordReviewEditor = forwardRef<WordReviewEditorRef, WordReviewEditor
3753
3754
  geometryFacet={activeRuntime.geometry}
3754
3755
  pageChromeHeaderBandPx={isPageWorkspace ? 32 : 0}
3755
3756
  pageChromeFooterBandPx={isPageWorkspace ? 32 : 0}
3756
- pageChromeInterGapPx={isPageWorkspace ? 24 : 16}
3757
+ pageChromeInterGapPx={PAGE_CHROME_DEFAULTS.interGapPx}
3757
3758
  onOpenHeaderStoryForPage={(pageIndex: number) =>
3758
3759
  openStoryForPage(activeRuntime, pageIndex, "header")
3759
3760
  }
@@ -25,6 +25,7 @@ import {
25
25
  } from "./chrome-overlay-projector";
26
26
  import { TwScopeCard } from "./tw-scope-card";
27
27
  import { useLocalSurfaceRequest } from "../chrome/local-surface-arbiter";
28
+ import { useUiApi } from "../ui-api-context";
28
29
  import type {
29
30
  EditorRole,
30
31
  ScopeCardModel,
@@ -46,12 +47,14 @@ export interface TwScopeCardLayerProps {
46
47
  */
47
48
  facet: WordReviewEditorLayoutFacet;
48
49
  /**
49
- * Workflow facet — canonical source of scope card models
50
- * (`runtime.workflow.getAllScopeCardModels()`). `null` is treated as
51
- * "no cards".
50
+ * Workflow facet — fallback source for no-provider paths. Mounted
51
+ * editor surfaces read scope cards through `api.ui.scope.card(...)`.
52
+ * `null` is treated as "no cards".
52
53
  */
53
54
  workflowFacet: WorkflowFacet | null;
54
55
  activeScopeId: string | null;
56
+ /** Scope ids currently visible under the Workflow rail layer filters. */
57
+ visibleScopeIds?: ReadonlySet<string>;
55
58
  onClose: () => void;
56
59
  onModeChange: (scopeId: string, mode: WorkflowScopeMode) => void;
57
60
  onIssueAction: (
@@ -91,6 +94,7 @@ export const TwScopeCardLayer: React.FC<TwScopeCardLayerProps> = ({
91
94
  facet,
92
95
  workflowFacet,
93
96
  activeScopeId,
97
+ visibleScopeIds,
94
98
  onClose,
95
99
  onModeChange,
96
100
  onIssueAction,
@@ -104,6 +108,7 @@ export const TwScopeCardLayer: React.FC<TwScopeCardLayerProps> = ({
104
108
  "data-testid": testId,
105
109
  }) => {
106
110
  const [pinnedScopeId, setPinnedScopeId] = React.useState<string | null>(null);
111
+ const ui = useUiApi();
107
112
 
108
113
  // If the layer's close handler fires, also clear any pin so the
109
114
  // next stripe click starts from a clean state.
@@ -116,14 +121,27 @@ export const TwScopeCardLayer: React.FC<TwScopeCardLayerProps> = ({
116
121
  setPinnedScopeId((current) => (current === scopeId ? null : scopeId));
117
122
  }, []);
118
123
 
124
+ const getWorkflowScopeCardModel = React.useCallback(
125
+ (scopeId: string): ScopeCardModel | null =>
126
+ workflowFacet?.getAllScopeCardModels().find((m) => m.scopeId === scopeId) ??
127
+ null,
128
+ [workflowFacet],
129
+ );
130
+
131
+ const getVisibleScopeCardModel = React.useCallback(
132
+ (scopeId: string | null): ScopeCardModel | null => {
133
+ if (!scopeId) return null;
134
+ if (visibleScopeIds && !visibleScopeIds.has(scopeId)) return null;
135
+ if (ui) return ui.scope.card(scopeId);
136
+ return getWorkflowScopeCardModel(scopeId);
137
+ },
138
+ [getWorkflowScopeCardModel, ui, visibleScopeIds],
139
+ );
140
+
119
141
  // The effective scope is the pinned one if it still resolves to a
120
142
  // model, else the active one. When a pinned scope disappears
121
143
  // (e.g. the host cleared the overlay), drop the pin.
122
- const models = workflowFacet?.getAllScopeCardModels() ?? [];
123
-
124
- const pinnedModel = pinnedScopeId
125
- ? models.find((m) => m.scopeId === pinnedScopeId) ?? null
126
- : null;
144
+ const pinnedModel = getVisibleScopeCardModel(pinnedScopeId);
127
145
 
128
146
  // R2.b — when a pinned scope disappears from the model list (e.g. the
129
147
  // host cleared the overlay), drop the pin. Must run in an effect, not
@@ -132,10 +150,10 @@ export const TwScopeCardLayer: React.FC<TwScopeCardLayerProps> = ({
132
150
  // exceeded" in React 18 concurrent mode where renders are retried
133
151
  // before commit.
134
152
  React.useEffect(() => {
135
- if (pinnedScopeId && !models.find((m) => m.scopeId === pinnedScopeId)) {
153
+ if (pinnedScopeId && !pinnedModel) {
136
154
  setPinnedScopeId(null);
137
155
  }
138
- }, [pinnedScopeId, models]);
156
+ }, [pinnedScopeId, pinnedModel]);
139
157
 
140
158
  const effectiveScopeId = pinnedModel ? pinnedScopeId : activeScopeId;
141
159
  const isPinnedEffective = pinnedScopeId !== null && pinnedScopeId === effectiveScopeId;
@@ -157,8 +175,7 @@ export const TwScopeCardLayer: React.FC<TwScopeCardLayerProps> = ({
157
175
  );
158
176
 
159
177
  if (!effectiveScopeId) return null;
160
- const model: ScopeCardModel | undefined =
161
- pinnedModel ?? models.find((m) => m.scopeId === effectiveScopeId);
178
+ const model = pinnedModel ?? getVisibleScopeCardModel(effectiveScopeId);
162
179
  if (!model) return null;
163
180
  if (!isArbiterActive) return null;
164
181
 
@@ -340,7 +340,7 @@ export interface TwProseMirrorSurfaceProps {
340
340
  pageChromeHeaderBandPx?: number;
341
341
  /** Height in px of each page's footer band. Default 32. */
342
342
  pageChromeFooterBandPx?: number;
343
- /** Visible vertical gap between adjacent pages. Default 24 for page mode. */
343
+ /** Visible vertical gap between adjacent pages. Default 48 to match the render kernel. */
344
344
  pageChromeInterGapPx?: number;
345
345
  /**
346
346
  * Revision counter; incremented by the host to trigger a decoration