@beyondwork/docx-react-component 1.0.18 → 1.0.20
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 +8 -2
- package/package.json +24 -34
- package/src/api/README.md +5 -1
- package/src/api/public-types.ts +710 -4
- package/src/api/session-state.ts +60 -0
- package/src/core/commands/formatting-commands.ts +2 -1
- package/src/core/commands/image-commands.ts +147 -0
- package/src/core/commands/index.ts +19 -3
- package/src/core/commands/list-commands.ts +231 -36
- package/src/core/commands/paragraph-layout-commands.ts +339 -0
- package/src/core/commands/section-layout-commands.ts +680 -0
- package/src/core/commands/style-commands.ts +262 -0
- package/src/core/search/search-text.ts +357 -0
- package/src/core/selection/mapping.ts +41 -0
- package/src/core/state/editor-state.ts +4 -1
- package/src/index.ts +51 -0
- package/src/io/docx-session.ts +623 -56
- package/src/io/export/serialize-comments.ts +104 -34
- package/src/io/export/serialize-footnotes.ts +198 -1
- package/src/io/export/serialize-headers-footers.ts +203 -10
- package/src/io/export/serialize-main-document.ts +285 -8
- package/src/io/export/serialize-numbering.ts +28 -7
- package/src/io/export/split-review-boundaries.ts +181 -19
- package/src/io/normalize/normalize-text.ts +144 -32
- package/src/io/ooxml/highlight-colors.ts +39 -0
- package/src/io/ooxml/numbering-sentinels.ts +44 -0
- package/src/io/ooxml/parse-comments.ts +85 -19
- package/src/io/ooxml/parse-fields.ts +396 -0
- package/src/io/ooxml/parse-footnotes.ts +452 -22
- package/src/io/ooxml/parse-headers-footers.ts +657 -29
- package/src/io/ooxml/parse-inline-media.ts +30 -0
- package/src/io/ooxml/parse-main-document.ts +807 -20
- package/src/io/ooxml/parse-numbering.ts +7 -0
- package/src/io/ooxml/parse-revisions.ts +317 -38
- package/src/io/ooxml/parse-settings.ts +184 -0
- package/src/io/ooxml/parse-shapes.ts +25 -0
- package/src/io/ooxml/parse-styles.ts +463 -0
- package/src/io/ooxml/parse-theme.ts +32 -0
- package/src/legal/bookmarks.ts +44 -0
- package/src/legal/cross-references.ts +59 -1
- package/src/model/canonical-document.ts +250 -4
- package/src/model/cds-1.0.0.ts +13 -0
- package/src/model/snapshot.ts +87 -2
- package/src/review/store/revision-store.ts +6 -0
- package/src/review/store/revision-types.ts +1 -0
- package/src/runtime/document-layout.ts +332 -0
- package/src/runtime/document-navigation.ts +603 -0
- package/src/runtime/document-runtime.ts +1754 -78
- package/src/runtime/document-search.ts +145 -0
- package/src/runtime/numbering-prefix.ts +47 -26
- package/src/runtime/page-layout-estimation.ts +212 -0
- package/src/runtime/read-only-diagnostics-runtime.ts +9 -0
- package/src/runtime/session-capabilities.ts +35 -3
- package/src/runtime/story-context.ts +164 -0
- package/src/runtime/story-targeting.ts +162 -0
- package/src/runtime/surface-projection.ts +324 -36
- package/src/runtime/table-schema.ts +89 -7
- package/src/runtime/view-state.ts +477 -0
- package/src/runtime/workflow-markup.ts +349 -0
- package/src/ui/WordReviewEditor.tsx +2469 -1344
- package/src/ui/browser-export.ts +52 -0
- package/src/ui/editor-command-bag.ts +120 -0
- package/src/ui/editor-runtime-boundary.ts +1422 -0
- package/src/ui/editor-shell-view.tsx +134 -0
- package/src/ui/editor-surface-controller.tsx +51 -0
- package/src/ui/headless/preserve-editor-selection.ts +5 -0
- package/src/ui/headless/revision-decoration-model.ts +4 -4
- package/src/ui/headless/selection-helpers.ts +20 -0
- package/src/ui/headless/selection-toolbar-model.ts +22 -0
- package/src/ui/headless/use-editor-keyboard.ts +6 -1
- package/src/ui/runtime-snapshot-selectors.ts +197 -0
- package/src/ui-tailwind/chrome/tw-alert-banner.tsx +18 -2
- package/src/ui-tailwind/chrome/tw-image-context-toolbar.tsx +129 -0
- package/src/ui-tailwind/chrome/tw-layout-panel.tsx +114 -0
- package/src/ui-tailwind/chrome/tw-object-context-toolbar.tsx +34 -0
- package/src/ui-tailwind/chrome/tw-page-ruler.tsx +386 -0
- package/src/ui-tailwind/chrome/tw-selection-toolbar.tsx +150 -14
- package/src/ui-tailwind/chrome/tw-table-context-toolbar.tsx +128 -0
- package/src/ui-tailwind/editor-surface/perf-probe.ts +179 -0
- package/src/ui-tailwind/editor-surface/pm-command-bridge.ts +46 -7
- package/src/ui-tailwind/editor-surface/pm-contextual-ui.ts +31 -0
- package/src/ui-tailwind/editor-surface/pm-decorations.ts +35 -0
- package/src/ui-tailwind/editor-surface/pm-position-map.ts +3 -3
- package/src/ui-tailwind/editor-surface/pm-schema.ts +186 -13
- package/src/ui-tailwind/editor-surface/pm-state-from-snapshot.ts +191 -68
- package/src/ui-tailwind/editor-surface/search-plugin.ts +19 -68
- package/src/ui-tailwind/editor-surface/surface-build-keys.ts +51 -0
- package/src/ui-tailwind/editor-surface/tw-inline-token.tsx +11 -0
- package/src/ui-tailwind/editor-surface/tw-opaque-block.tsx +7 -1
- package/src/ui-tailwind/editor-surface/tw-prosemirror-surface.tsx +528 -85
- package/src/ui-tailwind/editor-surface/tw-table-node-view.tsx +0 -1
- package/src/ui-tailwind/index.ts +2 -1
- package/src/ui-tailwind/page-chrome-model.ts +27 -0
- package/src/ui-tailwind/review/tw-comment-sidebar.tsx +277 -147
- package/src/ui-tailwind/review/tw-health-panel.tsx +31 -2
- package/src/ui-tailwind/review/tw-review-rail.tsx +8 -8
- package/src/ui-tailwind/review/tw-revision-sidebar.tsx +15 -15
- package/src/ui-tailwind/theme/editor-theme.css +127 -0
- package/src/ui-tailwind/toolbar/tw-toolbar-icon-button.tsx +4 -0
- package/src/ui-tailwind/toolbar/tw-toolbar.tsx +829 -12
- package/src/ui-tailwind/tw-review-workspace.tsx +1238 -42
- package/src/validation/compatibility-engine.ts +119 -24
- package/src/validation/compatibility-report.ts +1 -0
- package/src/validation/diagnostics.ts +1 -0
- package/src/validation/docx-comment-proof.ts +707 -0
|
@@ -4,6 +4,8 @@ import type { CommentDecorationModel } from "../../ui/headless/comment-decoratio
|
|
|
4
4
|
import { getCommentHighlightClass, type MarkupDisplay } from "../../ui/headless/comment-decoration-model";
|
|
5
5
|
import type { RevisionDecorationModel } from "../../ui/headless/revision-decoration-model";
|
|
6
6
|
import { getRevisionHighlightClass } from "../../ui/headless/revision-decoration-model";
|
|
7
|
+
import type { EditorStoryTarget, WorkflowScope } from "../../api/public-types";
|
|
8
|
+
import { MAIN_STORY_TARGET, storyTargetsEqual } from "../../core/selection/mapping.ts";
|
|
7
9
|
import type { PositionMap } from "./pm-position-map";
|
|
8
10
|
import type { Node as PMNode } from "prosemirror-model";
|
|
9
11
|
|
|
@@ -21,6 +23,8 @@ export function buildDecorations(
|
|
|
21
23
|
revisionModel: RevisionDecorationModel | undefined,
|
|
22
24
|
markupDisplay: MarkupDisplay,
|
|
23
25
|
showTrackedChanges = true,
|
|
26
|
+
workflowScopes?: readonly WorkflowScope[],
|
|
27
|
+
activeStory: EditorStoryTarget = MAIN_STORY_TARGET,
|
|
24
28
|
): DecorationSet {
|
|
25
29
|
const decorations: Decoration[] = [];
|
|
26
30
|
|
|
@@ -94,5 +98,36 @@ export function buildDecorations(
|
|
|
94
98
|
}
|
|
95
99
|
}
|
|
96
100
|
|
|
101
|
+
// Walk workflow scopes and create inline decorations for scope emphasis.
|
|
102
|
+
if (workflowScopes) {
|
|
103
|
+
for (const scope of workflowScopes) {
|
|
104
|
+
const scopeStoryTarget = scope.storyTarget ?? MAIN_STORY_TARGET;
|
|
105
|
+
if (!storyTargetsEqual(scopeStoryTarget, activeStory)) continue;
|
|
106
|
+
if (scope.anchor.kind === "detached") continue;
|
|
107
|
+
const from = scope.anchor.kind === "range" ? scope.anchor.from : scope.anchor.at;
|
|
108
|
+
const to = scope.anchor.kind === "range" ? scope.anchor.to : scope.anchor.at;
|
|
109
|
+
const pmFrom = positionMap.runtimeToPm(from);
|
|
110
|
+
const pmTo = positionMap.runtimeToPm(to);
|
|
111
|
+
if (pmFrom >= pmTo) continue;
|
|
112
|
+
|
|
113
|
+
const modeClass =
|
|
114
|
+
scope.mode === "edit"
|
|
115
|
+
? "bg-blue-50/40 ring-1 ring-blue-200/50"
|
|
116
|
+
: scope.mode === "suggest"
|
|
117
|
+
? "bg-amber-50/40 ring-1 ring-amber-200/50"
|
|
118
|
+
: scope.mode === "comment"
|
|
119
|
+
? "bg-green-50/40 ring-1 ring-green-200/50"
|
|
120
|
+
: "bg-gray-50/40 ring-1 ring-gray-200/50";
|
|
121
|
+
|
|
122
|
+
decorations.push(
|
|
123
|
+
Decoration.inline(pmFrom, pmTo, {
|
|
124
|
+
class: modeClass,
|
|
125
|
+
"data-workflow-scope-id": scope.scopeId,
|
|
126
|
+
"data-workflow-scope-mode": scope.mode,
|
|
127
|
+
}),
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
97
132
|
return DecorationSet.create(doc, decorations);
|
|
98
133
|
}
|
|
@@ -25,11 +25,11 @@ export function buildPositionMap(surface: EditorSurfaceSnapshot): PositionMap {
|
|
|
25
25
|
return entries[0]?.pmStart ?? 1;
|
|
26
26
|
}
|
|
27
27
|
if (runtimePos >= runtimeStorySize) {
|
|
28
|
-
return pmDocSize - 1;
|
|
28
|
+
return entries[entries.length - 1]?.pmEnd ?? pmDocSize - 1;
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
for (const entry of entries) {
|
|
32
|
-
if (runtimePos >= entry.runtimeStart && runtimePos
|
|
32
|
+
if (runtimePos >= entry.runtimeStart && runtimePos < entry.runtimeEnd) {
|
|
33
33
|
return entry.pmStart + (runtimePos - entry.runtimeStart);
|
|
34
34
|
}
|
|
35
35
|
if (runtimePos < entry.runtimeStart) {
|
|
@@ -75,7 +75,7 @@ function walkBlocks(
|
|
|
75
75
|
for (const block of blocks) {
|
|
76
76
|
switch (block.kind) {
|
|
77
77
|
case "paragraph": {
|
|
78
|
-
const pmContentStart = nextPmCursor
|
|
78
|
+
const pmContentStart = nextPmCursor;
|
|
79
79
|
const runtimeLength = block.to - block.from;
|
|
80
80
|
entries.push({
|
|
81
81
|
runtimeStart: block.from,
|
|
@@ -16,14 +16,21 @@ function resolveHeadingLevel(
|
|
|
16
16
|
): number | null {
|
|
17
17
|
if (styleId) {
|
|
18
18
|
const normalized = styleId.toLowerCase();
|
|
19
|
-
const
|
|
19
|
+
const compact = normalized.replace(/[\s_-]+/g, "");
|
|
20
|
+
const headingMatch = /^heading([1-6])$/.exec(compact);
|
|
20
21
|
if (headingMatch) {
|
|
21
22
|
return Number.parseInt(headingMatch[1], 10);
|
|
22
23
|
}
|
|
23
|
-
if (
|
|
24
|
+
if (compact === "title") {
|
|
24
25
|
return 1;
|
|
25
26
|
}
|
|
26
|
-
if (
|
|
27
|
+
if (compact === "subtitle") {
|
|
28
|
+
return 2;
|
|
29
|
+
}
|
|
30
|
+
if (compact === "tocheading") {
|
|
31
|
+
return 1;
|
|
32
|
+
}
|
|
33
|
+
if (/^(appendix|schedule|annex|exhibit|attachment)(heading|title)$/.test(compact)) {
|
|
27
34
|
return 2;
|
|
28
35
|
}
|
|
29
36
|
}
|
|
@@ -74,6 +81,24 @@ function safeCssColor(raw: string | null | undefined): string | null {
|
|
|
74
81
|
return null;
|
|
75
82
|
}
|
|
76
83
|
|
|
84
|
+
function sanitizeLinkHref(raw: string | null | undefined): string | null {
|
|
85
|
+
if (!raw) return null;
|
|
86
|
+
const trimmed = raw.trim();
|
|
87
|
+
if (trimmed.startsWith("#")) {
|
|
88
|
+
return trimmed;
|
|
89
|
+
}
|
|
90
|
+
const lower = trimmed.toLowerCase();
|
|
91
|
+
if (
|
|
92
|
+
lower.startsWith("http://") ||
|
|
93
|
+
lower.startsWith("https://") ||
|
|
94
|
+
lower.startsWith("mailto:") ||
|
|
95
|
+
lower.startsWith("tel:")
|
|
96
|
+
) {
|
|
97
|
+
return trimmed;
|
|
98
|
+
}
|
|
99
|
+
return null;
|
|
100
|
+
}
|
|
101
|
+
|
|
77
102
|
/**
|
|
78
103
|
* ProseMirror schema for the supported live surface slice.
|
|
79
104
|
*
|
|
@@ -95,14 +120,20 @@ export const editorSchema = new Schema({
|
|
|
95
120
|
numberingInstanceId: { default: null },
|
|
96
121
|
numberingLevel: { default: null },
|
|
97
122
|
numberingPrefix: { default: null },
|
|
123
|
+
numberingSuffix: { default: null },
|
|
98
124
|
alignment: { default: null },
|
|
99
125
|
spacingBefore: { default: null },
|
|
100
126
|
spacingAfter: { default: null },
|
|
101
127
|
lineSpacing: { default: null },
|
|
102
128
|
lineRule: { default: null },
|
|
129
|
+
contextualSpacing: { default: null },
|
|
130
|
+
listContinuation: { default: null },
|
|
131
|
+
contextualSpacingBefore: { default: null },
|
|
132
|
+
contextualSpacingAfter: { default: null },
|
|
103
133
|
indentLeft: { default: null },
|
|
104
134
|
indentRight: { default: null },
|
|
105
135
|
indentFirstLine: { default: null },
|
|
136
|
+
indentHanging: { default: null },
|
|
106
137
|
shadingFill: { default: null },
|
|
107
138
|
borderTop: { default: null },
|
|
108
139
|
borderBottom: { default: null },
|
|
@@ -127,9 +158,13 @@ export const editorSchema = new Schema({
|
|
|
127
158
|
const safeAlign = alignment === "both" ? "justify" : alignment;
|
|
128
159
|
if (safeAlign && SAFE_ALIGNMENT.has(safeAlign)) styles.push(`text-align: ${safeAlign}`);
|
|
129
160
|
const spacingBefore = node.attrs.spacingBefore as number | null;
|
|
130
|
-
|
|
161
|
+
const contextualSpacingBefore = node.attrs.contextualSpacingBefore as boolean | null;
|
|
162
|
+
if (contextualSpacingBefore) styles.push("margin-top: 0");
|
|
163
|
+
else if (spacingBefore) styles.push(`margin-top: ${spacingBefore / 20}px`);
|
|
164
|
+
const contextualSpacingAfter = node.attrs.contextualSpacingAfter as boolean | null;
|
|
131
165
|
const spacingAfter = node.attrs.spacingAfter as number | null;
|
|
132
|
-
if (
|
|
166
|
+
if (contextualSpacingAfter) styles.push("margin-bottom: 0");
|
|
167
|
+
else if (spacingAfter) styles.push(`margin-bottom: ${spacingAfter / 20}px`);
|
|
133
168
|
const lineSpacing = node.attrs.lineSpacing as number | null;
|
|
134
169
|
const lineRule = node.attrs.lineRule as string | null;
|
|
135
170
|
if (lineSpacing && lineRule === "auto") styles.push(`line-height: ${lineSpacing / 240}`);
|
|
@@ -140,7 +175,9 @@ export const editorSchema = new Schema({
|
|
|
140
175
|
const indentRight = node.attrs.indentRight as number | null;
|
|
141
176
|
if (indentRight) styles.push(`padding-right: ${indentRight / 20}px`);
|
|
142
177
|
const indentFirstLine = node.attrs.indentFirstLine as number | null;
|
|
143
|
-
|
|
178
|
+
const indentHanging = node.attrs.indentHanging as number | null;
|
|
179
|
+
if (indentHanging) styles.push(`text-indent: -${indentHanging / 20}px`);
|
|
180
|
+
else if (indentFirstLine) styles.push(`text-indent: ${indentFirstLine / 20}px`);
|
|
144
181
|
const shadingColor = safeHexColor(node.attrs.shadingFill as string | null);
|
|
145
182
|
if (shadingColor) styles.push(`background-color: ${shadingColor}`);
|
|
146
183
|
for (const [side, attrName] of [["top", "borderTop"], ["bottom", "borderBottom"], ["left", "borderLeft"], ["right", "borderRight"]] as const) {
|
|
@@ -159,9 +196,28 @@ export const editorSchema = new Schema({
|
|
|
159
196
|
if (headingLevel) {
|
|
160
197
|
attrs["data-heading-level"] = String(headingLevel);
|
|
161
198
|
}
|
|
199
|
+
const isNumbered = node.attrs.numberingInstanceId !== null;
|
|
200
|
+
if (isNumbered) {
|
|
201
|
+
attrs["data-numbered"] = "true";
|
|
202
|
+
}
|
|
203
|
+
const contextualSpacing = node.attrs.contextualSpacing as boolean | null;
|
|
204
|
+
if (contextualSpacing) {
|
|
205
|
+
attrs["data-contextual-spacing"] = "true";
|
|
206
|
+
}
|
|
207
|
+
const listContinuation = node.attrs.listContinuation as boolean | null;
|
|
208
|
+
if (listContinuation) {
|
|
209
|
+
attrs["data-list-continuation"] = "true";
|
|
210
|
+
}
|
|
211
|
+
if (contextualSpacingBefore) {
|
|
212
|
+
attrs["data-contextual-spacing-before"] = "true";
|
|
213
|
+
}
|
|
214
|
+
if (contextualSpacingAfter) {
|
|
215
|
+
attrs["data-contextual-spacing-after"] = "true";
|
|
216
|
+
}
|
|
162
217
|
if (styles.length > 0) attrs.style = styles.join("; ");
|
|
163
218
|
const numberingPrefix = node.attrs.numberingPrefix as string | null;
|
|
164
219
|
const numberingLevel = node.attrs.numberingLevel as number | null;
|
|
220
|
+
const numberingSuffix = node.attrs.numberingSuffix as "tab" | "space" | "nothing" | null;
|
|
165
221
|
const children: Array<string | number | readonly unknown[]> = [];
|
|
166
222
|
if (pageBreak) {
|
|
167
223
|
children.push([
|
|
@@ -177,6 +233,7 @@ export const editorSchema = new Schema({
|
|
|
177
233
|
}
|
|
178
234
|
if (numberingPrefix) {
|
|
179
235
|
const minWidth = Math.min(Math.max(numberingPrefix.length + 1, 4), 14);
|
|
236
|
+
const marginRight = numberingSuffix === "nothing" ? "0.25rem" : numberingSuffix === "space" ? "0.5rem" : "0.75rem";
|
|
180
237
|
children.push([
|
|
181
238
|
"span",
|
|
182
239
|
{
|
|
@@ -187,7 +244,8 @@ export const editorSchema = new Schema({
|
|
|
187
244
|
...(typeof numberingLevel === "number"
|
|
188
245
|
? { "data-numbering-level": String(numberingLevel) }
|
|
189
246
|
: {}),
|
|
190
|
-
|
|
247
|
+
...(numberingSuffix ? { "data-numbering-suffix": numberingSuffix } : {}),
|
|
248
|
+
style: `min-width: ${minWidth}ch; margin-right: ${marginRight}; font-variant-numeric: tabular-nums;`,
|
|
191
249
|
},
|
|
192
250
|
numberingPrefix,
|
|
193
251
|
]);
|
|
@@ -273,10 +331,43 @@ export const editorSchema = new Schema({
|
|
|
273
331
|
state: { default: "editable" },
|
|
274
332
|
display: { default: "inline" },
|
|
275
333
|
detail: { default: null },
|
|
334
|
+
src: { default: null },
|
|
335
|
+
widthEmu: { default: null },
|
|
336
|
+
heightEmu: { default: null },
|
|
276
337
|
},
|
|
277
338
|
toDOM(node) {
|
|
278
339
|
const isMissing = node.attrs.state === "missing";
|
|
279
340
|
const isFloating = node.attrs.display === "floating";
|
|
341
|
+
const src = node.attrs.src as string | null;
|
|
342
|
+
const widthEmu = node.attrs.widthEmu as number | null;
|
|
343
|
+
const heightEmu = node.attrs.heightEmu as number | null;
|
|
344
|
+
if (!isMissing && src) {
|
|
345
|
+
const widthPx = widthEmu ? Math.max(24, Math.round(widthEmu / 9525)) : undefined;
|
|
346
|
+
const heightPx = heightEmu ? Math.max(24, Math.round(heightEmu / 9525)) : undefined;
|
|
347
|
+
const style = [
|
|
348
|
+
"display:inline-block",
|
|
349
|
+
"vertical-align:middle",
|
|
350
|
+
"margin:0 4px",
|
|
351
|
+
widthPx ? `width:${widthPx}px` : "",
|
|
352
|
+
heightPx ? `height:${heightPx}px` : "",
|
|
353
|
+
].filter(Boolean).join(";");
|
|
354
|
+
return [
|
|
355
|
+
"span",
|
|
356
|
+
{
|
|
357
|
+
class: "inline-flex items-center rounded",
|
|
358
|
+
"data-node-type": "image",
|
|
359
|
+
title: (node.attrs.detail as string) ?? (node.attrs.altText as string) ?? "Image",
|
|
360
|
+
},
|
|
361
|
+
[
|
|
362
|
+
"img",
|
|
363
|
+
{
|
|
364
|
+
src,
|
|
365
|
+
alt: (node.attrs.altText as string) ?? "",
|
|
366
|
+
style,
|
|
367
|
+
},
|
|
368
|
+
],
|
|
369
|
+
];
|
|
370
|
+
}
|
|
280
371
|
return [
|
|
281
372
|
"span",
|
|
282
373
|
{
|
|
@@ -300,14 +391,28 @@ export const editorSchema = new Schema({
|
|
|
300
391
|
alias: { default: null },
|
|
301
392
|
tag: { default: null },
|
|
302
393
|
lock: { default: null },
|
|
394
|
+
checkboxChecked: { default: null },
|
|
395
|
+
dateValue: { default: null },
|
|
396
|
+
dropdownItems: { default: null },
|
|
397
|
+
comboBoxItems: { default: null },
|
|
398
|
+
showingPlcHdr: { default: false },
|
|
303
399
|
},
|
|
304
400
|
toDOM(node) {
|
|
305
|
-
const
|
|
401
|
+
const sdtType = node.attrs.sdtType as string | null;
|
|
402
|
+
const typeLabel = sdtType === "checkbox" ? "\u2611 Checkbox"
|
|
403
|
+
: sdtType === "date" ? "\uD83D\uDCC5 Date"
|
|
404
|
+
: sdtType === "dropDownList" ? "\u25BE Dropdown"
|
|
405
|
+
: sdtType === "comboBox" ? "\u25BE Combo box"
|
|
406
|
+
: sdtType === "plainText" ? "\u270E Plain text"
|
|
407
|
+
: sdtType === "richText" ? "\u270E Rich text"
|
|
408
|
+
: sdtType ?? undefined;
|
|
409
|
+
const meta = [node.attrs.alias, node.attrs.tag, typeLabel].filter(Boolean).join(" \u00B7 ");
|
|
306
410
|
return [
|
|
307
411
|
"section",
|
|
308
412
|
{
|
|
309
413
|
class: "my-2 rounded-xl border border-primary/15 bg-surface-raised/60 px-3 py-2",
|
|
310
414
|
"data-node-type": "sdt_block",
|
|
415
|
+
...(sdtType ? { "data-sdt-type": sdtType } : {}),
|
|
311
416
|
},
|
|
312
417
|
[
|
|
313
418
|
"div",
|
|
@@ -332,13 +437,29 @@ export const editorSchema = new Schema({
|
|
|
332
437
|
warningId: { default: "" },
|
|
333
438
|
label: { default: "Locked" },
|
|
334
439
|
detail: { default: "" },
|
|
440
|
+
presentation: { default: "inline-chip" },
|
|
335
441
|
},
|
|
336
442
|
toDOM(node) {
|
|
443
|
+
const presentation = node.attrs.presentation as string;
|
|
444
|
+
if (presentation === "quiet-marker") {
|
|
445
|
+
return [
|
|
446
|
+
"span",
|
|
447
|
+
{
|
|
448
|
+
class: "inline-block h-0 w-0 overflow-hidden align-baseline",
|
|
449
|
+
"data-node-type": "opaque_inline",
|
|
450
|
+
"data-inline-presentation": "quiet-marker",
|
|
451
|
+
contenteditable: "false",
|
|
452
|
+
title: node.attrs.detail as string,
|
|
453
|
+
"aria-label": node.attrs.label as string,
|
|
454
|
+
},
|
|
455
|
+
];
|
|
456
|
+
}
|
|
337
457
|
return [
|
|
338
458
|
"span",
|
|
339
459
|
{
|
|
340
460
|
class: "inline-flex items-center gap-1 mx-0.5 px-1.5 py-0.5 rounded text-xs text-comment bg-warning-soft",
|
|
341
461
|
"data-node-type": "opaque_inline",
|
|
462
|
+
"data-inline-presentation": "inline-chip",
|
|
342
463
|
title: node.attrs.detail as string,
|
|
343
464
|
},
|
|
344
465
|
"\uD83D\uDD12 " + (node.attrs.label as string),
|
|
@@ -346,6 +467,39 @@ export const editorSchema = new Schema({
|
|
|
346
467
|
},
|
|
347
468
|
},
|
|
348
469
|
|
|
470
|
+
field_ref_atom: {
|
|
471
|
+
inline: true,
|
|
472
|
+
group: "inline",
|
|
473
|
+
atom: true,
|
|
474
|
+
selectable: false,
|
|
475
|
+
attrs: {
|
|
476
|
+
fieldFamily: { default: "REF" },
|
|
477
|
+
fieldTarget: { default: null },
|
|
478
|
+
instruction: { default: "" },
|
|
479
|
+
refreshStatus: { default: "stale" },
|
|
480
|
+
label: { default: "Field" },
|
|
481
|
+
},
|
|
482
|
+
toDOM(node) {
|
|
483
|
+
const refreshStatus = node.attrs.refreshStatus as string;
|
|
484
|
+
const statusClass =
|
|
485
|
+
refreshStatus === "current"
|
|
486
|
+
? "text-blue-700 bg-blue-50 border-blue-200"
|
|
487
|
+
: refreshStatus === "unresolvable"
|
|
488
|
+
? "text-amber-800 bg-amber-50 border-amber-200"
|
|
489
|
+
: "text-slate-700 bg-slate-50 border-slate-200";
|
|
490
|
+
return [
|
|
491
|
+
"span",
|
|
492
|
+
{
|
|
493
|
+
class: `inline-flex items-center gap-1 mx-0.5 px-1.5 py-0.5 rounded text-xs border ${statusClass}`,
|
|
494
|
+
"data-node-type": "field_ref_atom",
|
|
495
|
+
"data-field-family": node.attrs.fieldFamily as string,
|
|
496
|
+
title: node.attrs.instruction as string,
|
|
497
|
+
},
|
|
498
|
+
(node.attrs.label as string) || "Field",
|
|
499
|
+
];
|
|
500
|
+
},
|
|
501
|
+
},
|
|
502
|
+
|
|
349
503
|
table: tableNodeSpec,
|
|
350
504
|
table_row: tableRowNodeSpec,
|
|
351
505
|
table_cell: tableCellNodeSpec,
|
|
@@ -403,7 +557,7 @@ export const editorSchema = new Schema({
|
|
|
403
557
|
inline: true,
|
|
404
558
|
group: "inline",
|
|
405
559
|
atom: true,
|
|
406
|
-
selectable:
|
|
560
|
+
selectable: true,
|
|
407
561
|
attrs: {
|
|
408
562
|
text: { default: null },
|
|
409
563
|
geometry: { default: null },
|
|
@@ -454,7 +608,7 @@ export const editorSchema = new Schema({
|
|
|
454
608
|
inline: true,
|
|
455
609
|
group: "inline",
|
|
456
610
|
atom: true,
|
|
457
|
-
selectable:
|
|
611
|
+
selectable: true,
|
|
458
612
|
attrs: {
|
|
459
613
|
text: { default: null },
|
|
460
614
|
shapeType: { default: null },
|
|
@@ -551,7 +705,15 @@ export const editorSchema = new Schema({
|
|
|
551
705
|
},
|
|
552
706
|
vanish: {
|
|
553
707
|
toDOM() {
|
|
554
|
-
return [
|
|
708
|
+
return [
|
|
709
|
+
"span",
|
|
710
|
+
{
|
|
711
|
+
style: "display: none",
|
|
712
|
+
"data-hidden-text": "true",
|
|
713
|
+
"aria-hidden": "true",
|
|
714
|
+
},
|
|
715
|
+
0,
|
|
716
|
+
];
|
|
555
717
|
},
|
|
556
718
|
},
|
|
557
719
|
emboss: {
|
|
@@ -682,15 +844,26 @@ export const editorSchema = new Schema({
|
|
|
682
844
|
{
|
|
683
845
|
tag: "a[href]",
|
|
684
846
|
getAttrs(dom) {
|
|
685
|
-
return { href: (dom as HTMLElement).getAttribute("href") };
|
|
847
|
+
return { href: sanitizeLinkHref((dom as HTMLElement).getAttribute("href")) ?? "" };
|
|
686
848
|
},
|
|
687
849
|
},
|
|
688
850
|
],
|
|
689
851
|
toDOM(mark) {
|
|
852
|
+
const href = sanitizeLinkHref(mark.attrs.href as string);
|
|
853
|
+
if (!href) {
|
|
854
|
+
return [
|
|
855
|
+
"span",
|
|
856
|
+
{
|
|
857
|
+
class: "text-accent underline decoration-1 underline-offset-2",
|
|
858
|
+
"data-invalid-link": "true",
|
|
859
|
+
},
|
|
860
|
+
0,
|
|
861
|
+
];
|
|
862
|
+
}
|
|
690
863
|
return [
|
|
691
864
|
"a",
|
|
692
865
|
{
|
|
693
|
-
href
|
|
866
|
+
href,
|
|
694
867
|
class: "text-accent underline decoration-1 underline-offset-2",
|
|
695
868
|
target: "_blank",
|
|
696
869
|
rel: "noopener noreferrer",
|