@beyondwork/docx-react-component 1.0.35 → 1.0.37
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/README.md +103 -13
- package/package.json +1 -1
- package/src/api/package-version.ts +13 -0
- package/src/api/public-types.ts +84 -1
- package/src/core/commands/index.ts +19 -2
- package/src/core/selection/mapping.ts +6 -0
- package/src/io/docx-session.ts +24 -9
- package/src/io/export/build-app-properties-xml.ts +88 -0
- package/src/io/export/serialize-comments.ts +6 -1
- package/src/io/export/serialize-footnotes.ts +10 -9
- package/src/io/export/serialize-headers-footers.ts +11 -10
- package/src/io/export/serialize-main-document.ts +337 -50
- package/src/io/export/serialize-numbering.ts +115 -24
- package/src/io/export/serialize-tables.ts +13 -11
- package/src/io/export/table-properties-xml.ts +35 -16
- package/src/io/export/twip.ts +66 -0
- package/src/io/normalize/normalize-text.ts +5 -0
- package/src/io/ooxml/parse-footnotes.ts +2 -1
- package/src/io/ooxml/parse-headers-footers.ts +2 -1
- package/src/io/ooxml/parse-main-document.ts +21 -1
- package/src/legal/bookmarks.ts +78 -0
- package/src/model/canonical-document.ts +11 -0
- package/src/review/store/scope-tag-diff.ts +130 -0
- package/src/runtime/document-navigation.ts +1 -305
- package/src/runtime/document-runtime.ts +178 -16
- package/src/runtime/layout/docx-font-loader.ts +143 -0
- package/src/runtime/layout/index.ts +188 -0
- package/src/runtime/layout/inert-layout-facet.ts +45 -0
- package/src/runtime/layout/layout-engine-instance.ts +618 -0
- package/src/runtime/layout/layout-invalidation.ts +257 -0
- package/src/runtime/layout/layout-measurement-provider.ts +175 -0
- package/src/runtime/layout/measurement-backend-canvas.ts +307 -0
- package/src/runtime/layout/measurement-backend-empirical.ts +208 -0
- package/src/runtime/layout/page-fragment-mapper.ts +179 -0
- package/src/runtime/layout/page-graph.ts +433 -0
- package/src/runtime/layout/page-layout-snapshot-adapter.ts +70 -0
- package/src/runtime/layout/page-story-resolver.ts +195 -0
- package/src/runtime/layout/paginated-layout-engine.ts +788 -0
- package/src/runtime/layout/public-facet.ts +705 -0
- package/src/runtime/layout/resolved-formatting-document.ts +317 -0
- package/src/runtime/layout/resolved-formatting-state.ts +430 -0
- package/src/runtime/scope-tag-registry.ts +95 -0
- package/src/runtime/session-capabilities.ts +7 -4
- package/src/runtime/surface-projection.ts +1 -0
- package/src/runtime/text-ack-range.ts +49 -0
- package/src/ui/WordReviewEditor.tsx +15 -0
- package/src/ui/editor-runtime-boundary.ts +10 -1
- package/src/ui/editor-surface-controller.tsx +3 -0
- package/src/ui/headless/chrome-registry.ts +235 -0
- package/src/ui/headless/scoped-chrome-policy.ts +164 -0
- package/src/ui/headless/selection-tool-context.ts +2 -0
- package/src/ui/headless/selection-tool-resolver.ts +36 -17
- package/src/ui-tailwind/editor-surface/fast-text-edit-lane.ts +333 -0
- package/src/ui-tailwind/editor-surface/local-edit-session-state.ts +89 -0
- package/src/ui-tailwind/editor-surface/perf-probe.ts +21 -1
- package/src/ui-tailwind/editor-surface/pm-command-bridge.ts +8 -1
- package/src/ui-tailwind/editor-surface/pm-decorations.ts +73 -13
- package/src/ui-tailwind/editor-surface/predicted-position-map.ts +78 -0
- package/src/ui-tailwind/editor-surface/predicted-tag-preflight.ts +63 -0
- package/src/ui-tailwind/editor-surface/predicted-tx-gate.ts +39 -0
- package/src/ui-tailwind/editor-surface/tw-prosemirror-surface.tsx +173 -6
- package/src/ui-tailwind/theme/editor-theme.css +40 -14
- package/src/ui-tailwind/toolbar/tw-toolbar-icon-button.tsx +2 -2
- package/src/ui-tailwind/toolbar/tw-toolbar.tsx +235 -166
- package/src/ui-tailwind/tw-review-workspace.tsx +27 -1
|
@@ -34,6 +34,7 @@ import {
|
|
|
34
34
|
type DocumentRuntimeEvent,
|
|
35
35
|
type DocumentRuntime,
|
|
36
36
|
} from "../runtime/document-runtime.ts";
|
|
37
|
+
import { createInertLayoutFacet } from "../runtime/layout/index.ts";
|
|
37
38
|
import { loadDocxEditorSession } from "../io/docx-session.ts";
|
|
38
39
|
import {
|
|
39
40
|
decodePersistedSourcePackageBytes,
|
|
@@ -738,6 +739,7 @@ function createLoadingRuntimeBridge(input: {
|
|
|
738
739
|
viewState: EditorViewStateSnapshot;
|
|
739
740
|
navigation: DocumentNavigationSnapshot;
|
|
740
741
|
}): WordReviewEditorRuntime {
|
|
742
|
+
const inertLayoutFacet = createInertLayoutFacet();
|
|
741
743
|
const emptyFieldSnapshot: FieldSnapshot = {
|
|
742
744
|
totalCount: 0,
|
|
743
745
|
supportedCount: 0,
|
|
@@ -772,7 +774,13 @@ function createLoadingRuntimeBridge(input: {
|
|
|
772
774
|
getCanonicalDocument: () => input.sessionState.canonicalDocument,
|
|
773
775
|
getSourcePackage: () => input.sessionState.sourcePackage,
|
|
774
776
|
replaceText: () => undefined,
|
|
775
|
-
applyActiveStoryTextCommand: () =>
|
|
777
|
+
applyActiveStoryTextCommand: () => ({
|
|
778
|
+
kind: "rejected",
|
|
779
|
+
newRevisionToken: "",
|
|
780
|
+
blockedReasons: [
|
|
781
|
+
{ code: "runtime_loading", message: "Document runtime is not ready yet." },
|
|
782
|
+
],
|
|
783
|
+
}),
|
|
776
784
|
dispatch: () => undefined,
|
|
777
785
|
undo: () => undefined,
|
|
778
786
|
redo: () => undefined,
|
|
@@ -801,6 +809,7 @@ function createLoadingRuntimeBridge(input: {
|
|
|
801
809
|
setZoom: () => undefined,
|
|
802
810
|
getPageLayoutSnapshot: () => null,
|
|
803
811
|
getDocumentNavigationSnapshot: () => input.navigation,
|
|
812
|
+
layout: inertLayoutFacet,
|
|
804
813
|
getCurrentLocation: () => null,
|
|
805
814
|
getLocationForSelection: () => null,
|
|
806
815
|
getLocationForAnchor: () => null,
|
|
@@ -60,6 +60,9 @@ export interface EditorSurfaceControllerProps {
|
|
|
60
60
|
activeWorkflowWorkItemId?: string | null;
|
|
61
61
|
activeWorkflowScopeIds?: readonly string[];
|
|
62
62
|
workflowMetadata?: readonly WorkflowMetadataMarkup[];
|
|
63
|
+
dispatchRuntimeCommand?: (
|
|
64
|
+
command: import("../ui-tailwind/editor-surface/fast-text-edit-lane.ts").LaneRuntimeCommand,
|
|
65
|
+
) => import("../api/public-types.ts").TextCommandAck;
|
|
63
66
|
}
|
|
64
67
|
|
|
65
68
|
export const EditorSurfaceController = forwardRef<
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
import type { WordReviewEditorChromePreset } from "../../api/public-types";
|
|
2
|
+
import type { SelectionToolKind } from "./selection-tool-types";
|
|
3
|
+
|
|
4
|
+
export type ChromeSurface = "top-toolbar" | "selection-tool";
|
|
5
|
+
export type ChromeDensity = "compact";
|
|
6
|
+
export type ToolbarChromePlacement = "inline" | "overflow" | "hidden";
|
|
7
|
+
|
|
8
|
+
export type ToolbarChromeItemId =
|
|
9
|
+
| "history"
|
|
10
|
+
| "text-style-selectors"
|
|
11
|
+
| "inline-formatting"
|
|
12
|
+
| "text-colors"
|
|
13
|
+
| "paragraph-alignment"
|
|
14
|
+
| "list-actions"
|
|
15
|
+
| "indentation"
|
|
16
|
+
| "list-continuation"
|
|
17
|
+
| "insert-actions"
|
|
18
|
+
| "update-actions"
|
|
19
|
+
| "scope-status"
|
|
20
|
+
| "story-breadcrumb"
|
|
21
|
+
| "sidebar-toggle"
|
|
22
|
+
| "comment"
|
|
23
|
+
| "tracked-changes-toggle"
|
|
24
|
+
| "workspace-mode"
|
|
25
|
+
| "zoom"
|
|
26
|
+
| "health"
|
|
27
|
+
| "export";
|
|
28
|
+
|
|
29
|
+
export interface ChromeRegistryEntryBase {
|
|
30
|
+
id: string;
|
|
31
|
+
surfaces: ReadonlyArray<ChromeSurface>;
|
|
32
|
+
group: string;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface SelectionToolRegistryEntry extends ChromeRegistryEntryBase {
|
|
36
|
+
id: SelectionToolKind;
|
|
37
|
+
surfaces: ["selection-tool"];
|
|
38
|
+
precedence: number;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface ToolbarChromeRegistryEntry extends ChromeRegistryEntryBase {
|
|
42
|
+
id: ToolbarChromeItemId;
|
|
43
|
+
surfaces: ["top-toolbar"];
|
|
44
|
+
presets: ReadonlyArray<WordReviewEditorChromePreset>;
|
|
45
|
+
fullPlacement: Exclude<ToolbarChromePlacement, "hidden">;
|
|
46
|
+
compactPlacement: ToolbarChromePlacement;
|
|
47
|
+
runtimeBehavior: "always" | "formatting" | "structure" | "comment";
|
|
48
|
+
scopeBehavior?: "default" | "scoped-only" | "hidden-when-scoped";
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export const SELECTION_TOOL_REGISTRY: ReadonlyArray<SelectionToolRegistryEntry> = [
|
|
52
|
+
{ id: "suggestion-review", surfaces: ["selection-tool"], group: "review", precedence: 10 },
|
|
53
|
+
{ id: "comment-thread", surfaces: ["selection-tool"], group: "review", precedence: 20 },
|
|
54
|
+
{ id: "workflow-task", surfaces: ["selection-tool"], group: "workflow", precedence: 30 },
|
|
55
|
+
{ id: "structure-context", surfaces: ["selection-tool"], group: "structure", precedence: 40 },
|
|
56
|
+
{ id: "formatting-inline", surfaces: ["selection-tool"], group: "formatting", precedence: 50 },
|
|
57
|
+
{ id: "blocked-explainer", surfaces: ["selection-tool"], group: "blocked", precedence: 60 },
|
|
58
|
+
];
|
|
59
|
+
|
|
60
|
+
export const TOOLBAR_CHROME_REGISTRY: ReadonlyArray<ToolbarChromeRegistryEntry> = [
|
|
61
|
+
{
|
|
62
|
+
id: "history",
|
|
63
|
+
surfaces: ["top-toolbar"],
|
|
64
|
+
group: "history",
|
|
65
|
+
presets: ["simple", "advanced", "review"],
|
|
66
|
+
fullPlacement: "inline",
|
|
67
|
+
compactPlacement: "inline",
|
|
68
|
+
runtimeBehavior: "always",
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
id: "text-style-selectors",
|
|
72
|
+
surfaces: ["top-toolbar"],
|
|
73
|
+
group: "text",
|
|
74
|
+
presets: ["advanced"],
|
|
75
|
+
fullPlacement: "inline",
|
|
76
|
+
compactPlacement: "overflow",
|
|
77
|
+
runtimeBehavior: "formatting",
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
id: "inline-formatting",
|
|
81
|
+
surfaces: ["top-toolbar"],
|
|
82
|
+
group: "text",
|
|
83
|
+
presets: ["simple", "advanced", "review"],
|
|
84
|
+
fullPlacement: "inline",
|
|
85
|
+
compactPlacement: "inline",
|
|
86
|
+
runtimeBehavior: "formatting",
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
id: "text-colors",
|
|
90
|
+
surfaces: ["top-toolbar"],
|
|
91
|
+
group: "text",
|
|
92
|
+
presets: ["simple", "advanced"],
|
|
93
|
+
fullPlacement: "inline",
|
|
94
|
+
compactPlacement: "inline",
|
|
95
|
+
runtimeBehavior: "formatting",
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
id: "paragraph-alignment",
|
|
99
|
+
surfaces: ["top-toolbar"],
|
|
100
|
+
group: "paragraph",
|
|
101
|
+
presets: ["simple", "advanced"],
|
|
102
|
+
fullPlacement: "inline",
|
|
103
|
+
compactPlacement: "inline",
|
|
104
|
+
runtimeBehavior: "formatting",
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
id: "list-actions",
|
|
108
|
+
surfaces: ["top-toolbar"],
|
|
109
|
+
group: "paragraph",
|
|
110
|
+
presets: ["simple", "advanced"],
|
|
111
|
+
fullPlacement: "inline",
|
|
112
|
+
compactPlacement: "overflow",
|
|
113
|
+
runtimeBehavior: "formatting",
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
id: "indentation",
|
|
117
|
+
surfaces: ["top-toolbar"],
|
|
118
|
+
group: "paragraph",
|
|
119
|
+
presets: ["simple", "advanced"],
|
|
120
|
+
fullPlacement: "inline",
|
|
121
|
+
compactPlacement: "overflow",
|
|
122
|
+
runtimeBehavior: "formatting",
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
id: "list-continuation",
|
|
126
|
+
surfaces: ["top-toolbar"],
|
|
127
|
+
group: "paragraph",
|
|
128
|
+
presets: ["simple", "advanced"],
|
|
129
|
+
fullPlacement: "inline",
|
|
130
|
+
compactPlacement: "overflow",
|
|
131
|
+
runtimeBehavior: "formatting",
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
id: "insert-actions",
|
|
135
|
+
surfaces: ["top-toolbar"],
|
|
136
|
+
group: "document",
|
|
137
|
+
presets: ["simple", "advanced"],
|
|
138
|
+
fullPlacement: "inline",
|
|
139
|
+
compactPlacement: "overflow",
|
|
140
|
+
runtimeBehavior: "structure",
|
|
141
|
+
scopeBehavior: "hidden-when-scoped",
|
|
142
|
+
},
|
|
143
|
+
{
|
|
144
|
+
id: "update-actions",
|
|
145
|
+
surfaces: ["top-toolbar"],
|
|
146
|
+
group: "document",
|
|
147
|
+
presets: ["advanced"],
|
|
148
|
+
fullPlacement: "inline",
|
|
149
|
+
compactPlacement: "overflow",
|
|
150
|
+
runtimeBehavior: "structure",
|
|
151
|
+
scopeBehavior: "hidden-when-scoped",
|
|
152
|
+
},
|
|
153
|
+
{
|
|
154
|
+
id: "scope-status",
|
|
155
|
+
surfaces: ["top-toolbar"],
|
|
156
|
+
group: "scope",
|
|
157
|
+
presets: ["simple", "advanced", "review"],
|
|
158
|
+
fullPlacement: "inline",
|
|
159
|
+
compactPlacement: "inline",
|
|
160
|
+
runtimeBehavior: "always",
|
|
161
|
+
scopeBehavior: "scoped-only",
|
|
162
|
+
},
|
|
163
|
+
{
|
|
164
|
+
id: "story-breadcrumb",
|
|
165
|
+
surfaces: ["top-toolbar"],
|
|
166
|
+
group: "scope",
|
|
167
|
+
presets: ["simple", "advanced", "review"],
|
|
168
|
+
fullPlacement: "inline",
|
|
169
|
+
compactPlacement: "inline",
|
|
170
|
+
runtimeBehavior: "always",
|
|
171
|
+
},
|
|
172
|
+
{
|
|
173
|
+
id: "sidebar-toggle",
|
|
174
|
+
surfaces: ["top-toolbar"],
|
|
175
|
+
group: "review",
|
|
176
|
+
presets: ["simple", "advanced", "review"],
|
|
177
|
+
fullPlacement: "inline",
|
|
178
|
+
compactPlacement: "inline",
|
|
179
|
+
runtimeBehavior: "always",
|
|
180
|
+
},
|
|
181
|
+
{
|
|
182
|
+
id: "comment",
|
|
183
|
+
surfaces: ["top-toolbar"],
|
|
184
|
+
group: "review",
|
|
185
|
+
presets: ["simple", "advanced", "review"],
|
|
186
|
+
fullPlacement: "inline",
|
|
187
|
+
compactPlacement: "inline",
|
|
188
|
+
runtimeBehavior: "comment",
|
|
189
|
+
},
|
|
190
|
+
{
|
|
191
|
+
id: "tracked-changes-toggle",
|
|
192
|
+
surfaces: ["top-toolbar"],
|
|
193
|
+
group: "review",
|
|
194
|
+
presets: ["advanced", "review"],
|
|
195
|
+
fullPlacement: "inline",
|
|
196
|
+
compactPlacement: "inline",
|
|
197
|
+
runtimeBehavior: "always",
|
|
198
|
+
},
|
|
199
|
+
{
|
|
200
|
+
id: "workspace-mode",
|
|
201
|
+
surfaces: ["top-toolbar"],
|
|
202
|
+
group: "view",
|
|
203
|
+
presets: ["simple", "advanced", "review"],
|
|
204
|
+
fullPlacement: "inline",
|
|
205
|
+
compactPlacement: "inline",
|
|
206
|
+
runtimeBehavior: "always",
|
|
207
|
+
},
|
|
208
|
+
{
|
|
209
|
+
id: "zoom",
|
|
210
|
+
surfaces: ["top-toolbar"],
|
|
211
|
+
group: "view",
|
|
212
|
+
presets: ["simple", "advanced", "review"],
|
|
213
|
+
fullPlacement: "inline",
|
|
214
|
+
compactPlacement: "inline",
|
|
215
|
+
runtimeBehavior: "always",
|
|
216
|
+
},
|
|
217
|
+
{
|
|
218
|
+
id: "health",
|
|
219
|
+
surfaces: ["top-toolbar"],
|
|
220
|
+
group: "status",
|
|
221
|
+
presets: ["simple", "advanced", "review"],
|
|
222
|
+
fullPlacement: "inline",
|
|
223
|
+
compactPlacement: "inline",
|
|
224
|
+
runtimeBehavior: "always",
|
|
225
|
+
},
|
|
226
|
+
{
|
|
227
|
+
id: "export",
|
|
228
|
+
surfaces: ["top-toolbar"],
|
|
229
|
+
group: "status",
|
|
230
|
+
presets: ["simple", "advanced", "review"],
|
|
231
|
+
fullPlacement: "inline",
|
|
232
|
+
compactPlacement: "inline",
|
|
233
|
+
runtimeBehavior: "always",
|
|
234
|
+
},
|
|
235
|
+
];
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
ActiveListContext,
|
|
3
|
+
InteractionGuardSnapshot,
|
|
4
|
+
WorkflowScopeSnapshot,
|
|
5
|
+
WordReviewEditorChromePreset,
|
|
6
|
+
} from "../../api/public-types";
|
|
7
|
+
import type { SessionCapabilities } from "../../runtime/session-capabilities";
|
|
8
|
+
import {
|
|
9
|
+
TOOLBAR_CHROME_REGISTRY,
|
|
10
|
+
type ToolbarChromeItemId,
|
|
11
|
+
type ToolbarChromePlacement,
|
|
12
|
+
} from "./chrome-registry";
|
|
13
|
+
import type { SelectionToolKind } from "./selection-tool-types";
|
|
14
|
+
|
|
15
|
+
export interface ToolbarChromeItemPolicy {
|
|
16
|
+
visible: boolean;
|
|
17
|
+
enabled: boolean;
|
|
18
|
+
placement: ToolbarChromePlacement;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface ScopedChromePolicy {
|
|
22
|
+
density: "compact";
|
|
23
|
+
activeScopeId?: string;
|
|
24
|
+
activeWorkItemId?: string | null;
|
|
25
|
+
scopeStatusLabel?: string;
|
|
26
|
+
toolbar: Record<ToolbarChromeItemId, ToolbarChromeItemPolicy>;
|
|
27
|
+
selection: {
|
|
28
|
+
hiddenKinds: ReadonlyArray<SelectionToolKind>;
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface ResolveScopedChromePolicyInput {
|
|
33
|
+
preset: WordReviewEditorChromePreset;
|
|
34
|
+
compactMode: boolean;
|
|
35
|
+
capabilities?: SessionCapabilities;
|
|
36
|
+
interactionGuardSnapshot?: InteractionGuardSnapshot;
|
|
37
|
+
workflowScopeSnapshot?: WorkflowScopeSnapshot | null;
|
|
38
|
+
activeListContext?: ActiveListContext | null;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function resolveScopedChromePolicy(
|
|
42
|
+
input: ResolveScopedChromePolicyInput,
|
|
43
|
+
): ScopedChromePolicy {
|
|
44
|
+
const effectiveMode = input.interactionGuardSnapshot?.effectiveMode ?? "edit";
|
|
45
|
+
const canFormatText = Boolean(input.capabilities?.canEdit) && effectiveMode === "edit";
|
|
46
|
+
const canInsertStructural = Boolean(input.capabilities?.canEdit) && effectiveMode === "edit";
|
|
47
|
+
const canAddComment =
|
|
48
|
+
Boolean(input.capabilities?.canAddComment) &&
|
|
49
|
+
effectiveMode !== "view" &&
|
|
50
|
+
effectiveMode !== "blocked";
|
|
51
|
+
const activeScopeId = input.interactionGuardSnapshot?.matchedScopeId;
|
|
52
|
+
const activeWorkItemId = input.workflowScopeSnapshot?.activeWorkItemId ?? null;
|
|
53
|
+
const hasScopedContext = Boolean(activeScopeId || activeWorkItemId);
|
|
54
|
+
const scopeStatusLabel =
|
|
55
|
+
input.workflowScopeSnapshot?.activeWorkItem?.title ??
|
|
56
|
+
input.workflowScopeSnapshot?.scopes.find((scope) => scope.scopeId === activeScopeId)?.label ??
|
|
57
|
+
(activeScopeId && input.interactionGuardSnapshot?.matchedScopeMode
|
|
58
|
+
? `Scoped ${input.interactionGuardSnapshot.matchedScopeMode}`
|
|
59
|
+
: undefined);
|
|
60
|
+
|
|
61
|
+
const toolbar = Object.fromEntries(
|
|
62
|
+
TOOLBAR_CHROME_REGISTRY.map((entry) => {
|
|
63
|
+
let visible = entry.presets.includes(input.preset);
|
|
64
|
+
|
|
65
|
+
if (visible) {
|
|
66
|
+
switch (entry.runtimeBehavior) {
|
|
67
|
+
case "formatting":
|
|
68
|
+
visible = canFormatText;
|
|
69
|
+
break;
|
|
70
|
+
case "structure":
|
|
71
|
+
visible = canInsertStructural;
|
|
72
|
+
break;
|
|
73
|
+
case "comment":
|
|
74
|
+
visible = canAddComment;
|
|
75
|
+
break;
|
|
76
|
+
default:
|
|
77
|
+
visible = true;
|
|
78
|
+
break;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (visible && entry.id === "list-continuation") {
|
|
83
|
+
visible = Boolean(input.activeListContext);
|
|
84
|
+
}
|
|
85
|
+
if (visible && entry.id === "scope-status") {
|
|
86
|
+
visible = Boolean(scopeStatusLabel);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if (visible && entry.scopeBehavior === "scoped-only") {
|
|
90
|
+
visible = hasScopedContext;
|
|
91
|
+
}
|
|
92
|
+
if (visible && entry.scopeBehavior === "hidden-when-scoped") {
|
|
93
|
+
visible = !hasScopedContext;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const placement = visible
|
|
97
|
+
? (input.compactMode ? entry.compactPlacement : entry.fullPlacement)
|
|
98
|
+
: "hidden";
|
|
99
|
+
const enabled =
|
|
100
|
+
visible &&
|
|
101
|
+
(entry.id !== "tracked-changes-toggle" || Boolean(input.capabilities?.trackChangesSupported)) &&
|
|
102
|
+
(entry.id !== "export" || Boolean(input.capabilities?.canExport));
|
|
103
|
+
|
|
104
|
+
return [
|
|
105
|
+
entry.id,
|
|
106
|
+
{
|
|
107
|
+
visible,
|
|
108
|
+
enabled,
|
|
109
|
+
placement,
|
|
110
|
+
} satisfies ToolbarChromeItemPolicy,
|
|
111
|
+
];
|
|
112
|
+
}),
|
|
113
|
+
) as Record<ToolbarChromeItemId, ToolbarChromeItemPolicy>;
|
|
114
|
+
|
|
115
|
+
const hiddenKinds = resolveHiddenSelectionToolKinds(effectiveMode);
|
|
116
|
+
|
|
117
|
+
return {
|
|
118
|
+
density: "compact",
|
|
119
|
+
...(activeScopeId ? { activeScopeId } : {}),
|
|
120
|
+
activeWorkItemId,
|
|
121
|
+
...(scopeStatusLabel ? { scopeStatusLabel } : {}),
|
|
122
|
+
toolbar,
|
|
123
|
+
selection: {
|
|
124
|
+
hiddenKinds,
|
|
125
|
+
},
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export function isToolbarChromeItemVisible(
|
|
130
|
+
policy: ScopedChromePolicy,
|
|
131
|
+
itemId: ToolbarChromeItemId,
|
|
132
|
+
): boolean {
|
|
133
|
+
return policy.toolbar[itemId]?.visible ?? false;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export function getToolbarChromePlacement(
|
|
137
|
+
policy: ScopedChromePolicy,
|
|
138
|
+
itemId: ToolbarChromeItemId,
|
|
139
|
+
): ToolbarChromePlacement {
|
|
140
|
+
return policy.toolbar[itemId]?.placement ?? "hidden";
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
export function shouldRenderSelectionToolKind(
|
|
144
|
+
policy: ScopedChromePolicy | undefined,
|
|
145
|
+
kind: SelectionToolKind,
|
|
146
|
+
): boolean {
|
|
147
|
+
return !policy?.selection.hiddenKinds.includes(kind);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function resolveHiddenSelectionToolKinds(
|
|
151
|
+
effectiveMode: InteractionGuardSnapshot["effectiveMode"] | "edit",
|
|
152
|
+
): ReadonlyArray<SelectionToolKind> {
|
|
153
|
+
switch (effectiveMode) {
|
|
154
|
+
case "suggest":
|
|
155
|
+
return ["formatting-inline", "structure-context"];
|
|
156
|
+
case "comment":
|
|
157
|
+
return ["formatting-inline", "structure-context"];
|
|
158
|
+
case "view":
|
|
159
|
+
case "blocked":
|
|
160
|
+
return ["formatting-inline", "structure-context", "suggestion-review"];
|
|
161
|
+
default:
|
|
162
|
+
return [];
|
|
163
|
+
}
|
|
164
|
+
}
|
|
@@ -5,6 +5,7 @@ import type {
|
|
|
5
5
|
StyleCatalogSnapshot,
|
|
6
6
|
} from "../../api/public-types";
|
|
7
7
|
import type { SessionCapabilities } from "../../runtime/session-capabilities";
|
|
8
|
+
import type { ScopedChromePolicy } from "./scoped-chrome-policy";
|
|
8
9
|
import type { SelectionToolResolverContext } from "./selection-tool-types";
|
|
9
10
|
|
|
10
11
|
export interface SelectionToolResolverInput extends SelectionToolResolverContext {
|
|
@@ -16,4 +17,5 @@ export interface SelectionToolResolverInput extends SelectionToolResolverContext
|
|
|
16
17
|
preferListStructureContext?: boolean;
|
|
17
18
|
addCommentDisabledReason?: string;
|
|
18
19
|
suppressedSuggestionRevisionId?: string | null;
|
|
20
|
+
scopedChromePolicy?: ScopedChromePolicy;
|
|
19
21
|
}
|
|
@@ -17,27 +17,26 @@ import type {
|
|
|
17
17
|
SuggestionReviewSelectionToolModel,
|
|
18
18
|
WorkflowTaskSelectionToolModel,
|
|
19
19
|
} from "./selection-tool-types";
|
|
20
|
+
import {
|
|
21
|
+
SELECTION_TOOL_REGISTRY,
|
|
22
|
+
type SelectionToolRegistryEntry,
|
|
23
|
+
} from "./chrome-registry";
|
|
24
|
+
import { shouldRenderSelectionToolKind } from "./scoped-chrome-policy";
|
|
20
25
|
|
|
21
26
|
export function resolveActiveSelectionTool(
|
|
22
27
|
input: SelectionToolResolverInput,
|
|
23
28
|
): ActiveSelectionToolModel | null {
|
|
24
|
-
const
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
workflowTool ??
|
|
36
|
-
structureTool ??
|
|
37
|
-
formattingTool ??
|
|
38
|
-
blockedTool;
|
|
39
|
-
|
|
40
|
-
return resolvedTool;
|
|
29
|
+
for (const entry of SELECTION_TOOL_REGISTRY) {
|
|
30
|
+
if (!shouldRenderSelectionToolKind(input.scopedChromePolicy, entry.id)) {
|
|
31
|
+
continue;
|
|
32
|
+
}
|
|
33
|
+
const resolvedTool = buildSelectionToolFromRegistryEntry(entry, input);
|
|
34
|
+
if (resolvedTool) {
|
|
35
|
+
return resolvedTool;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return null;
|
|
41
40
|
}
|
|
42
41
|
|
|
43
42
|
export function buildFormattingInlineSelectionToolModel(
|
|
@@ -273,6 +272,26 @@ function selectionMatchesSuggestion(
|
|
|
273
272
|
);
|
|
274
273
|
}
|
|
275
274
|
|
|
275
|
+
function buildSelectionToolFromRegistryEntry(
|
|
276
|
+
entry: SelectionToolRegistryEntry,
|
|
277
|
+
input: SelectionToolResolverInput,
|
|
278
|
+
): ActiveSelectionToolModel | null {
|
|
279
|
+
switch (entry.id) {
|
|
280
|
+
case "suggestion-review":
|
|
281
|
+
return buildSuggestionReviewSelectionToolModel(input);
|
|
282
|
+
case "comment-thread":
|
|
283
|
+
return buildCommentThreadSelectionToolModel(input);
|
|
284
|
+
case "workflow-task":
|
|
285
|
+
return buildWorkflowTaskSelectionToolModel(input);
|
|
286
|
+
case "structure-context":
|
|
287
|
+
return buildStructureContextSelectionToolModel(input);
|
|
288
|
+
case "formatting-inline":
|
|
289
|
+
return buildFormattingInlineSelectionToolModel(input);
|
|
290
|
+
case "blocked-explainer":
|
|
291
|
+
return buildBlockedExplainerSelectionToolModel(input);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
276
295
|
function findSelectionMatchingScope(
|
|
277
296
|
activeRange: SelectionToolResolverInput["snapshot"]["selection"]["activeRange"] | null,
|
|
278
297
|
activeStory: EditorStoryTarget,
|