@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
|
@@ -1,23 +1,23 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Scope rail layer — renders workflow scopes as a
|
|
3
|
-
*
|
|
4
|
-
*
|
|
2
|
+
* Scope rail layer — renders workflow scopes as a thin color stripe in
|
|
3
|
+
* the reserved left-gutter lane plus a per-line flat tint behind the
|
|
4
|
+
* scoped text runs.
|
|
5
5
|
*
|
|
6
|
-
* Per runtime-rendering-and-chrome-phase.md §5
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
6
|
+
* Per runtime-rendering-and-chrome-phase.md §5 and
|
|
7
|
+
* docs/plans/scope-card-overlay.md P0, the rail is a projection over
|
|
8
|
+
* canonical workflow scopes; it never lives inside the PM NodeView
|
|
9
|
+
* tree. Positions come from the render kernel's per-line block data
|
|
10
|
+
* (walked directly from `RenderFrame.pages[].regions.body.blocks[]`)
|
|
11
|
+
* so multi-line scopes produce one tight tint per line rather than a
|
|
12
|
+
* fat bounding-box union.
|
|
11
13
|
*/
|
|
12
14
|
|
|
13
15
|
import * as React from "react";
|
|
14
16
|
import {
|
|
15
|
-
inflateRect,
|
|
16
17
|
projectRectToOverlay,
|
|
17
|
-
unionRect,
|
|
18
18
|
type OverlayCoordinateSpace,
|
|
19
19
|
} from "./chrome-overlay-projector";
|
|
20
|
-
import type { RenderFrameRect } from "../../runtime/render";
|
|
20
|
+
import type { RenderFrame, RenderFrameRect } from "../../runtime/render";
|
|
21
21
|
import type { ScopeRailSegment, ScopeRailPosture } from "../../runtime/layout";
|
|
22
22
|
import type { WordReviewEditorLayoutFacet } from "../../runtime/layout";
|
|
23
23
|
|
|
@@ -26,16 +26,25 @@ import type { WordReviewEditorLayoutFacet } from "../../runtime/layout";
|
|
|
26
26
|
// ---------------------------------------------------------------------------
|
|
27
27
|
|
|
28
28
|
export interface TwScopeRailLayerProps {
|
|
29
|
-
/** Layout facet that provides segments +
|
|
29
|
+
/** Layout facet that provides segments + render frame. */
|
|
30
30
|
facet: WordReviewEditorLayoutFacet;
|
|
31
31
|
/** Overlay's coordinate space. Defaults to the overlay's own origin. */
|
|
32
32
|
space?: OverlayCoordinateSpace;
|
|
33
|
-
/** Horizontal
|
|
33
|
+
/** Horizontal pad (px) the rail gutter occupies to the left of body. */
|
|
34
34
|
railLaneWidthPx?: number;
|
|
35
|
-
/** Optional click handler for a segment label (open-scope drawer, etc). */
|
|
36
|
-
onSegmentClick?: (segment: ScopeRailSegment) => void;
|
|
37
35
|
/** Scope id that should render with the `active` emphasis. */
|
|
38
36
|
activeScopeId?: string | null;
|
|
37
|
+
/**
|
|
38
|
+
* Fires when the user clicks the rail stripe — opens the scope card.
|
|
39
|
+
* P0 wires this directly; P1 replaces with card-layer-aware routing.
|
|
40
|
+
*/
|
|
41
|
+
onStripeClick?: (segment: ScopeRailSegment) => void;
|
|
42
|
+
/**
|
|
43
|
+
* Legacy click handler kept for existing consumers. Called alongside
|
|
44
|
+
* `onStripeClick` so host apps that subscribed to the pre-stripe API
|
|
45
|
+
* continue to receive clicks.
|
|
46
|
+
*/
|
|
47
|
+
onSegmentClick?: (segment: ScopeRailSegment) => void;
|
|
39
48
|
/** Test id applied to the layer root. */
|
|
40
49
|
"data-testid"?: string;
|
|
41
50
|
}
|
|
@@ -46,7 +55,7 @@ export interface TwScopeRailLayerProps {
|
|
|
46
55
|
|
|
47
56
|
interface PostureStyle {
|
|
48
57
|
labelText: string;
|
|
49
|
-
icon: string;
|
|
58
|
+
icon: string;
|
|
50
59
|
railToken: string;
|
|
51
60
|
tintToken: string;
|
|
52
61
|
}
|
|
@@ -65,20 +74,19 @@ const POSTURE_STYLES: Record<ScopeRailPosture, PostureStyle> = {
|
|
|
65
74
|
// Component
|
|
66
75
|
// ---------------------------------------------------------------------------
|
|
67
76
|
|
|
68
|
-
const DEFAULT_RAIL_LANE_PX =
|
|
69
|
-
const
|
|
77
|
+
const DEFAULT_RAIL_LANE_PX = 44;
|
|
78
|
+
const STRIPE_WIDTH_PX = 4;
|
|
79
|
+
const LABEL_WIDTH_PX = 40;
|
|
70
80
|
|
|
71
81
|
export const TwScopeRailLayer: React.FC<TwScopeRailLayerProps> = ({
|
|
72
82
|
facet,
|
|
73
83
|
space,
|
|
74
84
|
railLaneWidthPx = DEFAULT_RAIL_LANE_PX,
|
|
75
|
-
onSegmentClick,
|
|
76
85
|
activeScopeId,
|
|
86
|
+
onStripeClick,
|
|
87
|
+
onSegmentClick,
|
|
77
88
|
"data-testid": testId,
|
|
78
89
|
}) => {
|
|
79
|
-
// Read the render frame once per paint cycle. The facet.subscribe path
|
|
80
|
-
// already invalidates the caller's React state on layout changes, so we
|
|
81
|
-
// just read on render.
|
|
82
90
|
const frame = typeof facet.getRenderFrame === "function"
|
|
83
91
|
? facet.getRenderFrame() ?? null
|
|
84
92
|
: null;
|
|
@@ -88,16 +96,6 @@ export const TwScopeRailLayer: React.FC<TwScopeRailLayerProps> = ({
|
|
|
88
96
|
return null;
|
|
89
97
|
}
|
|
90
98
|
|
|
91
|
-
// Group segments by scopeId so multi-page scopes render one contiguous
|
|
92
|
-
// tint per page range. (Per-page render happens below because each
|
|
93
|
-
// scope may span pages.)
|
|
94
|
-
const items = segments.map((segment) => {
|
|
95
|
-
const rect = resolveSegmentRect(facet, frame, segment);
|
|
96
|
-
if (!rect) return null;
|
|
97
|
-
const style = POSTURE_STYLES[segment.posture];
|
|
98
|
-
return { segment, rect, style };
|
|
99
|
-
}).filter((item): item is NonNullable<typeof item> => item !== null);
|
|
100
|
-
|
|
101
99
|
const projectorSpace: OverlayCoordinateSpace = space ?? { originLeftPx: 0, originTopPx: 0 };
|
|
102
100
|
|
|
103
101
|
return (
|
|
@@ -108,43 +106,86 @@ export const TwScopeRailLayer: React.FC<TwScopeRailLayerProps> = ({
|
|
|
108
106
|
role="group"
|
|
109
107
|
aria-label="Workflow scope rail"
|
|
110
108
|
>
|
|
111
|
-
{
|
|
112
|
-
const
|
|
113
|
-
const
|
|
109
|
+
{segments.map((segment) => {
|
|
110
|
+
const style = POSTURE_STYLES[segment.posture];
|
|
111
|
+
const lineRects = collectLineRectsForSegment(frame, segment);
|
|
112
|
+
if (lineRects.length === 0) return null;
|
|
113
|
+
|
|
114
|
+
const isActive =
|
|
115
|
+
activeScopeId === segment.scopeId || segment.isActiveWorkItem;
|
|
116
|
+
|
|
117
|
+
// Stripe + label span the vertical range of the scope's lines;
|
|
118
|
+
// they live in the gutter lane to the left of the first line.
|
|
119
|
+
const firstLine = lineRects[0];
|
|
120
|
+
const lastLine = lineRects[lineRects.length - 1];
|
|
121
|
+
const stripeTopPx = firstLine.topPx;
|
|
122
|
+
const stripeHeightPx =
|
|
123
|
+
lastLine.topPx + lastLine.heightPx - firstLine.topPx;
|
|
124
|
+
const stripeRect: RenderFrameRect = {
|
|
125
|
+
leftPx: firstLine.leftPx - railLaneWidthPx + (railLaneWidthPx - STRIPE_WIDTH_PX) / 2,
|
|
126
|
+
topPx: stripeTopPx,
|
|
127
|
+
widthPx: STRIPE_WIDTH_PX,
|
|
128
|
+
heightPx: Math.max(stripeHeightPx, 14),
|
|
129
|
+
};
|
|
114
130
|
const labelRect: RenderFrameRect = {
|
|
115
|
-
leftPx:
|
|
116
|
-
topPx:
|
|
131
|
+
leftPx: firstLine.leftPx - railLaneWidthPx,
|
|
132
|
+
topPx: stripeTopPx,
|
|
117
133
|
widthPx: LABEL_WIDTH_PX,
|
|
118
|
-
heightPx:
|
|
134
|
+
heightPx: 20,
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
const handleActivate = () => {
|
|
138
|
+
onStripeClick?.(segment);
|
|
139
|
+
onSegmentClick?.(segment);
|
|
140
|
+
};
|
|
141
|
+
const handleStripeKey = (event: React.KeyboardEvent<HTMLButtonElement>) => {
|
|
142
|
+
if (event.key === "Enter" || event.key === " ") {
|
|
143
|
+
event.preventDefault();
|
|
144
|
+
handleActivate();
|
|
145
|
+
}
|
|
119
146
|
};
|
|
120
147
|
|
|
121
148
|
return (
|
|
122
149
|
<React.Fragment key={`${segment.scopeId}:${segment.pageIndex}:${segment.fromOffset}`}>
|
|
123
|
-
{/*
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
150
|
+
{/* Per-line tint behind the scoped text runs. */}
|
|
151
|
+
{lineRects.map((lineRect, index) => (
|
|
152
|
+
<div
|
|
153
|
+
key={`tint:${index}`}
|
|
154
|
+
className={`wre-scope-rail-tint wre-scope-rail-tint-${style.tintToken} ${
|
|
155
|
+
isActive ? "wre-scope-rail-tint-active" : ""
|
|
156
|
+
}`}
|
|
157
|
+
data-scope-id={segment.scopeId}
|
|
158
|
+
data-posture={segment.posture}
|
|
159
|
+
data-line-index={index}
|
|
160
|
+
style={projectRectToOverlay(lineRect, projectorSpace)}
|
|
161
|
+
/>
|
|
162
|
+
))}
|
|
163
|
+
{/* Rail stripe in the gutter. */}
|
|
133
164
|
<button
|
|
134
165
|
type="button"
|
|
135
|
-
className={`wre-scope-rail-
|
|
136
|
-
isActive ? "wre-scope-rail-
|
|
166
|
+
className={`wre-scope-rail-stripe wre-scope-rail-label-${style.railToken} ${
|
|
167
|
+
isActive ? "wre-scope-rail-stripe-active" : ""
|
|
137
168
|
}`}
|
|
138
169
|
data-scope-id={segment.scopeId}
|
|
139
170
|
data-posture={segment.posture}
|
|
140
|
-
data-
|
|
171
|
+
data-testid="scope-rail-stripe"
|
|
141
172
|
aria-label={`${style.labelText}${segment.label ? `: ${segment.label}` : ""}`}
|
|
142
|
-
|
|
173
|
+
aria-expanded={isActive ? "true" : "false"}
|
|
174
|
+
onClick={handleActivate}
|
|
175
|
+
onKeyDown={handleStripeKey}
|
|
176
|
+
style={projectRectToOverlay(stripeRect, projectorSpace)}
|
|
177
|
+
/>
|
|
178
|
+
{/* Label pill — revealed on stripe hover via CSS. */}
|
|
179
|
+
<div
|
|
180
|
+
className={`wre-scope-rail-label wre-scope-rail-label-${style.railToken}`}
|
|
181
|
+
data-scope-id={segment.scopeId}
|
|
182
|
+
data-posture={segment.posture}
|
|
183
|
+
aria-hidden="true"
|
|
143
184
|
style={projectRectToOverlay(labelRect, projectorSpace)}
|
|
144
185
|
>
|
|
145
186
|
<span aria-hidden="true" className={`wre-scope-rail-icon wre-scope-rail-icon-${style.icon}`} />
|
|
146
187
|
<span className="wre-scope-rail-label-text">{style.labelText}</span>
|
|
147
|
-
</
|
|
188
|
+
</div>
|
|
148
189
|
</React.Fragment>
|
|
149
190
|
);
|
|
150
191
|
})}
|
|
@@ -156,23 +197,53 @@ export const TwScopeRailLayer: React.FC<TwScopeRailLayerProps> = ({
|
|
|
156
197
|
// Internals
|
|
157
198
|
// ---------------------------------------------------------------------------
|
|
158
199
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
200
|
+
/**
|
|
201
|
+
* Walk the render frame and collect one rect per line whose owning
|
|
202
|
+
* fragment overlaps the segment's offset range. Line frames come from
|
|
203
|
+
* the kernel's per-line projection, which clamps widthPx to the line's
|
|
204
|
+
* text-actual width via `Math.min(regionFrame.widthPx, box.widthTwips
|
|
205
|
+
* * pxPerTwip)`. Multi-line scopes therefore produce one tight tint
|
|
206
|
+
* per line instead of a single bounding-box union.
|
|
207
|
+
*
|
|
208
|
+
* Exported for unit testing — not part of the public API.
|
|
209
|
+
*/
|
|
210
|
+
export function collectLineRectsForSegment(
|
|
211
|
+
frame: RenderFrame,
|
|
162
212
|
segment: ScopeRailSegment,
|
|
163
|
-
): RenderFrameRect
|
|
164
|
-
const
|
|
165
|
-
const
|
|
166
|
-
const
|
|
167
|
-
if (
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
213
|
+
): RenderFrameRect[] {
|
|
214
|
+
const rects: RenderFrameRect[] = [];
|
|
215
|
+
const lo = segment.fromOffset;
|
|
216
|
+
const hi = segment.toOffset;
|
|
217
|
+
if (lo >= hi) return rects;
|
|
218
|
+
|
|
219
|
+
const page = frame.pages[segment.pageIndex];
|
|
220
|
+
if (!page) {
|
|
221
|
+
// Fall back to scanning every page — protects against pageIndex
|
|
222
|
+
// drift during an in-flight layout update.
|
|
223
|
+
for (const p of frame.pages) {
|
|
224
|
+
pushRectsFromPage(rects, p, lo, hi);
|
|
225
|
+
}
|
|
226
|
+
return rects;
|
|
227
|
+
}
|
|
228
|
+
pushRectsFromPage(rects, page, lo, hi);
|
|
229
|
+
return rects;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
function pushRectsFromPage(
|
|
233
|
+
sink: RenderFrameRect[],
|
|
234
|
+
page: RenderFrame["pages"][number],
|
|
235
|
+
lo: number,
|
|
236
|
+
hi: number,
|
|
237
|
+
): void {
|
|
238
|
+
for (const block of page.regions.body.blocks) {
|
|
239
|
+
const from = block.fragment.from;
|
|
240
|
+
const to = block.fragment.to;
|
|
241
|
+
if (to <= lo || from >= hi) continue;
|
|
242
|
+
// Block overlaps the segment — emit one tint per line.
|
|
243
|
+
for (const line of block.lines) {
|
|
244
|
+
sink.push(line.frame);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
176
247
|
}
|
|
177
248
|
|
|
178
249
|
export default TwScopeRailLayer;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { DocumentPageSnapshot, SurfaceBlockSnapshot } from "../../api/public-types.ts";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Filter surface blocks to those that overlap a page's character-offset range.
|
|
5
|
+
* A block overlaps [startOffset, endOffset) when block.from < endOffset AND block.to > startOffset.
|
|
6
|
+
* Blocks that straddle a page boundary appear on both pages.
|
|
7
|
+
*/
|
|
8
|
+
export function sliceBlocksForPage(
|
|
9
|
+
blocks: SurfaceBlockSnapshot[],
|
|
10
|
+
page: Pick<DocumentPageSnapshot, "startOffset" | "endOffset">,
|
|
11
|
+
): SurfaceBlockSnapshot[] {
|
|
12
|
+
return blocks.filter(
|
|
13
|
+
(b) => b.from < page.endOffset && b.to > page.startOffset,
|
|
14
|
+
);
|
|
15
|
+
}
|
|
@@ -23,6 +23,8 @@ export interface CommandBridgeCallbacks extends SelectionSyncCallbacks {
|
|
|
23
23
|
onInsertHardBreak: () => void;
|
|
24
24
|
onInsertTab: () => void;
|
|
25
25
|
onOutdentTab?: () => void;
|
|
26
|
+
onListIndent?: () => void;
|
|
27
|
+
onListOutdent?: () => void;
|
|
26
28
|
onUndo: () => void;
|
|
27
29
|
onRedo: () => void;
|
|
28
30
|
onBlockedInput?: (command: "paste" | "drop", message: string) => void;
|
|
@@ -77,8 +79,14 @@ export function createSelectionSyncPlugin(
|
|
|
77
79
|
});
|
|
78
80
|
}
|
|
79
81
|
|
|
82
|
+
export interface CommandBridgePluginOptions {
|
|
83
|
+
/** P6: when true, omit prosemirror-tables columnResizing() — chrome overlay grips take over. */
|
|
84
|
+
useChromeColumnResizing?: boolean;
|
|
85
|
+
}
|
|
86
|
+
|
|
80
87
|
export function createCommandBridgePlugins(
|
|
81
88
|
callbacks: CommandBridgeCallbacks,
|
|
89
|
+
options?: CommandBridgePluginOptions,
|
|
82
90
|
): Plugin[] {
|
|
83
91
|
let isComposing = false;
|
|
84
92
|
|
|
@@ -145,6 +153,18 @@ export function createCommandBridgePlugins(
|
|
|
145
153
|
return false;
|
|
146
154
|
}
|
|
147
155
|
|
|
156
|
+
// Alt+Shift+Right → list indent; Alt+Shift+Left → list outdent (Word keyboard behavior)
|
|
157
|
+
if (event.altKey && event.shiftKey) {
|
|
158
|
+
if (event.key === "ArrowRight") {
|
|
159
|
+
callbacks.onListIndent?.();
|
|
160
|
+
return true;
|
|
161
|
+
}
|
|
162
|
+
if (event.key === "ArrowLeft") {
|
|
163
|
+
callbacks.onListOutdent?.();
|
|
164
|
+
return true;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
148
168
|
const resolution = resolveSurfaceShortcut(
|
|
149
169
|
{
|
|
150
170
|
key: event.key,
|
|
@@ -195,7 +215,12 @@ export function createCommandBridgePlugins(
|
|
|
195
215
|
// selection handles. Doc-changing table transactions (new rows, etc.) are
|
|
196
216
|
// filtered by the runtime filter above; navigation-only steps pass through.
|
|
197
217
|
const tablePlugin = tableEditing();
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
218
|
+
// P6: chrome overlay grips replace prosemirror-tables column resizing.
|
|
219
|
+
// useChromeColuumnResizing=true removes the PM plugin; set to false to fall
|
|
220
|
+
// back to the legacy prosemirror-tables drag handle for one release cycle.
|
|
221
|
+
const plugins: Plugin[] = [filterPlugin, selectionPlugin, inputPlugin, keydownPlugin, tablePlugin];
|
|
222
|
+
if (!options?.useChromeColumnResizing) {
|
|
223
|
+
plugins.push(columnResizing());
|
|
224
|
+
}
|
|
225
|
+
return plugins;
|
|
201
226
|
}
|
|
@@ -39,6 +39,12 @@ function getWorkflowInlineClass(
|
|
|
39
39
|
isActiveWorkItem: boolean,
|
|
40
40
|
isSelectionZone: boolean,
|
|
41
41
|
): string {
|
|
42
|
+
// Active-work-item emphasis lives on the ChromeOverlay rail stripe, not
|
|
43
|
+
// on the PM inline decoration. The per-run outline produced a halo
|
|
44
|
+
// around every text fragment (one box per wrapped span due to
|
|
45
|
+
// box-decoration-break: clone) — the `data-workflow-active` attribute
|
|
46
|
+
// remains the canonical hook for hit-testing and accessibility.
|
|
47
|
+
void isActiveWorkItem;
|
|
42
48
|
const base =
|
|
43
49
|
scope.mode === "edit"
|
|
44
50
|
? "wre-workflow-inline wre-workflow-inline-edit"
|
|
@@ -47,8 +53,7 @@ function getWorkflowInlineClass(
|
|
|
47
53
|
: scope.mode === "comment"
|
|
48
54
|
? "wre-workflow-inline wre-workflow-inline-comment"
|
|
49
55
|
: "wre-workflow-inline wre-workflow-inline-view";
|
|
50
|
-
|
|
51
|
-
return isActiveWorkItem ? `${withZone} wre-workflow-inline-active` : withZone;
|
|
56
|
+
return isSelectionZone ? `${base} wre-workflow-inline-zone` : base;
|
|
52
57
|
}
|
|
53
58
|
|
|
54
59
|
function getWorkflowRailClass(
|