@stll/folio-core 0.9.0 → 0.10.0
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/dist/ai-edits/headless.d.ts +52 -12
- package/dist/ai-edits/headless.js +181 -55
- package/dist/ai-edits/index.d.ts +2 -2
- package/dist/ai-edits/index.js +2 -2
- package/dist/controller/layoutPipeline.js +2 -1
- package/dist/document-operations.d.ts +11 -2
- package/dist/document-operations.js +7 -1
- package/dist/document-stories.d.ts +10 -0
- package/dist/document-stories.js +27 -0
- package/dist/docx/corePropertiesParser.d.ts +8 -0
- package/dist/docx/corePropertiesParser.js +53 -0
- package/dist/docx/index.d.ts +2 -1
- package/dist/docx/index.js +2 -1
- package/dist/docx/metadataPrivacy.d.ts +40 -0
- package/dist/docx/metadataPrivacy.js +131 -0
- package/dist/docx/parser.js +4 -1
- package/dist/docx/rezip.d.ts +5 -4
- package/dist/docx/rezip.js +6 -8
- package/dist/layout-bridge/convert/toFlowBlocks.js +1 -0
- package/dist/layout-engine/measure/cache.js +2 -7
- package/dist/layout-engine/measure/effectiveLineBreakPolicy.d.ts +29 -0
- package/dist/layout-engine/measure/effectiveLineBreakPolicy.js +60 -0
- package/dist/layout-engine/measure/measureParagraph.js +32 -36
- package/dist/layout-engine/measure/tableCellFloating.js +39 -33
- package/dist/layout-engine/types.d.ts +5 -3
- package/dist/prosemirror/attrs/index.js +1 -0
- package/dist/prosemirror/conversion/effectiveTableCellFormatting.d.ts +62 -0
- package/dist/prosemirror/conversion/effectiveTableCellFormatting.js +131 -0
- package/dist/prosemirror/conversion/fromProseDoc.js +1 -0
- package/dist/prosemirror/conversion/toProseDoc.js +40 -68
- package/dist/prosemirror/extensions/core/ParagraphExtension.js +1 -1
- package/dist/prosemirror/extensions/marks/HighlightExtension.js +1 -1
- package/dist/prosemirror/extensions/marks/RunShadingExtension.js +1 -1
- package/dist/prosemirror/extensions/marks/TextColorExtension.js +1 -1
- package/dist/prosemirror/extensions/nodes/ImageExtension.js +1 -0
- package/dist/prosemirror/extensions/nodes/TableExtension.js +1 -1
- package/dist/prosemirror/schema/nodes.d.ts +2 -1
- package/dist/redline.d.ts +26 -11
- package/dist/redline.js +95 -62
- package/dist/server.d.ts +5 -4
- package/dist/server.js +5 -4
- package/dist/version-comparison.d.ts +65 -11
- package/dist/version-comparison.js +187 -29
- package/package.json +1 -1
|
@@ -3,6 +3,36 @@ import { isFloatingImageRun } from "../types.js";
|
|
|
3
3
|
import { clampFloatingWrapMargins } from "./clampFloatingWrapMargins.js";
|
|
4
4
|
import { createTableCellFlowState, placeTableCellBlock } from "./tableCellFlow.js";
|
|
5
5
|
//#region src/layout-engine/measure/tableCellFloating.ts
|
|
6
|
+
const resolveCellScopedPosition = ({ run, paragraphY, contentWidth }) => {
|
|
7
|
+
const position = run.position;
|
|
8
|
+
let side = "left";
|
|
9
|
+
let x = 0;
|
|
10
|
+
if (position?.horizontal) {
|
|
11
|
+
const horizontal = position.horizontal;
|
|
12
|
+
if (horizontal.align === "right") {
|
|
13
|
+
side = "right";
|
|
14
|
+
x = contentWidth - run.width;
|
|
15
|
+
} else if (horizontal.align === "center") x = (contentWidth - run.width) / 2;
|
|
16
|
+
else if (horizontal.posOffset !== void 0) {
|
|
17
|
+
x = emuToPixels(horizontal.posOffset);
|
|
18
|
+
side = x > contentWidth / 2 ? "right" : "left";
|
|
19
|
+
}
|
|
20
|
+
} else if (run.cssFloat === "right") {
|
|
21
|
+
side = "right";
|
|
22
|
+
x = contentWidth - run.width;
|
|
23
|
+
}
|
|
24
|
+
let y = paragraphY;
|
|
25
|
+
if (position?.vertical) {
|
|
26
|
+
const vertical = position.vertical;
|
|
27
|
+
if (vertical.posOffset !== void 0) y = paragraphY + emuToPixels(vertical.posOffset);
|
|
28
|
+
else if (vertical.align === "top") y = 0;
|
|
29
|
+
}
|
|
30
|
+
return {
|
|
31
|
+
side,
|
|
32
|
+
x,
|
|
33
|
+
y
|
|
34
|
+
};
|
|
35
|
+
};
|
|
6
36
|
function getTableCellContentWidth(cell, cellMeasure) {
|
|
7
37
|
const padLeft = cell?.padding?.left ?? 7;
|
|
8
38
|
const padRight = cell?.padding?.right ?? 7;
|
|
@@ -19,40 +49,16 @@ function getTableCellFloatingImages(cell, cellMeasure, contentWidth, resolvePosi
|
|
|
19
49
|
if (block.kind !== "paragraph") continue;
|
|
20
50
|
for (const run of block.runs) {
|
|
21
51
|
if (run.kind !== "image" || !isFloatingImageRun(run)) continue;
|
|
22
|
-
const position = run.position;
|
|
23
52
|
const distTop = run.distTop ?? 0;
|
|
24
53
|
const distBottom = run.distBottom ?? 0;
|
|
25
54
|
const distLeft = run.distLeft ?? 12;
|
|
26
55
|
const distRight = run.distRight ?? 12;
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
x = 0;
|
|
34
|
-
if (position?.horizontal) {
|
|
35
|
-
const horizontal = position.horizontal;
|
|
36
|
-
if (horizontal.align === "right") {
|
|
37
|
-
side = "right";
|
|
38
|
-
x = contentWidth - run.width;
|
|
39
|
-
} else if (horizontal.align === "center") x = (contentWidth - run.width) / 2;
|
|
40
|
-
else if (horizontal.posOffset !== void 0) {
|
|
41
|
-
x = emuToPixels(horizontal.posOffset);
|
|
42
|
-
side = x > contentWidth / 2 ? "right" : "left";
|
|
43
|
-
}
|
|
44
|
-
} else if (run.cssFloat === "right") {
|
|
45
|
-
side = "right";
|
|
46
|
-
x = contentWidth - run.width;
|
|
47
|
-
}
|
|
48
|
-
y = placement.contentTop;
|
|
49
|
-
if (position?.vertical) {
|
|
50
|
-
const vertical = position.vertical;
|
|
51
|
-
if (vertical.posOffset !== void 0) y = placement.contentTop + emuToPixels(vertical.posOffset);
|
|
52
|
-
else if (vertical.align === "top") y = 0;
|
|
53
|
-
}
|
|
54
|
-
x = Math.max(0, Math.min(x, contentWidth - run.width));
|
|
55
|
-
}
|
|
56
|
+
const verticalOriginY = run.position?.vertical?.relativeTo === "paragraph" ? placement.top : placement.contentTop;
|
|
57
|
+
const resolved = run.layoutInCell === false && resolvePosition ? resolvePosition(run, verticalOriginY) : resolveCellScopedPosition({
|
|
58
|
+
run,
|
|
59
|
+
paragraphY: verticalOriginY,
|
|
60
|
+
contentWidth
|
|
61
|
+
});
|
|
56
62
|
let wrapText = "bothSides";
|
|
57
63
|
if (run.cssFloat === "left") wrapText = "right";
|
|
58
64
|
else if (run.cssFloat === "right") wrapText = "left";
|
|
@@ -67,9 +73,9 @@ function getTableCellFloatingImages(cell, cellMeasure, contentWidth, resolvePosi
|
|
|
67
73
|
...run.cropRight != null ? { cropRight: run.cropRight } : {},
|
|
68
74
|
...run.cropBottom != null ? { cropBottom: run.cropBottom } : {},
|
|
69
75
|
...run.cropLeft != null ? { cropLeft: run.cropLeft } : {},
|
|
70
|
-
x,
|
|
71
|
-
y,
|
|
72
|
-
side,
|
|
76
|
+
x: resolved.x,
|
|
77
|
+
y: resolved.y,
|
|
78
|
+
side: resolved.side,
|
|
73
79
|
distTop,
|
|
74
80
|
distBottom,
|
|
75
81
|
distLeft,
|
|
@@ -162,7 +162,8 @@ type ImageRun = {
|
|
|
162
162
|
* eigenpal #424 (opacity render pipeline).
|
|
163
163
|
*/
|
|
164
164
|
opacity?: number; /** Position for floating/anchored images */
|
|
165
|
-
position?: ImageRunPosition; /**
|
|
165
|
+
position?: ImageRunPosition; /** Whether a table-cell anchor uses the cell as its positioning scope. Undefined defaults true. */
|
|
166
|
+
layoutInCell?: boolean; /** Wrap type from DOCX (inline, square, tight, through, topAndBottom, etc.) */
|
|
166
167
|
wrapType?: ImageWrap["type"]; /** Display mode for CSS rendering */
|
|
167
168
|
displayMode?: "inline" | "block" | "float"; /** CSS float direction */
|
|
168
169
|
cssFloat?: "left" | "right" | "none"; /** Wrap distances in pixels */
|
|
@@ -293,13 +294,14 @@ type ListNumPr = {
|
|
|
293
294
|
*/
|
|
294
295
|
type ParagraphAttrs = {
|
|
295
296
|
alignment?: "left" | "center" | "right" | "justify"; /** East Asian first/last-character restrictions (`w:kinsoku`). */
|
|
296
|
-
kinsoku?: boolean; /** Hanging punctuation (`w:overflowPunct`)
|
|
297
|
+
kinsoku?: boolean; /** Hanging punctuation (`w:overflowPunct`); defaults to enabled when omitted. */
|
|
297
298
|
overflowPunctuation?: boolean; /** Exempt this paragraph from document automatic hyphenation. */
|
|
298
299
|
suppressAutoHyphens?: boolean; /** Document-wide automatic hyphenation controls retained for line layout. */
|
|
299
300
|
automaticHyphenation?: {
|
|
300
301
|
enabled: true;
|
|
301
302
|
doNotHyphenateCaps?: boolean;
|
|
302
|
-
consecutiveLineLimit?: number;
|
|
303
|
+
consecutiveLineLimit?: number; /** Maximum tolerated line-end whitespace before hyphenation, in twips. */
|
|
304
|
+
hyphenationZoneTwips?: number;
|
|
303
305
|
}; /** Document-wide custom line-edge characters and compatibility behavior. */
|
|
304
306
|
lineBreakRules?: {
|
|
305
307
|
noLineBreaksBefore?: {
|
|
@@ -306,6 +306,7 @@ const readImageAttrs = (node) => {
|
|
|
306
306
|
optionalNumber(attrs, "distLeft", "image.attrs.distLeft", issues);
|
|
307
307
|
optionalNumber(attrs, "distRight", "image.attrs.distRight", issues);
|
|
308
308
|
optionalImagePosition(attrs, "position", "image.attrs.position", issues);
|
|
309
|
+
optionalBoolean(attrs, "layoutInCell", "image.attrs.layoutInCell", issues);
|
|
309
310
|
optionalNumber(attrs, "borderWidth", "image.attrs.borderWidth", issues);
|
|
310
311
|
optionalString(attrs, "borderColor", "image.attrs.borderColor", issues);
|
|
311
312
|
optionalString(attrs, "borderStyle", "image.attrs.borderStyle", issues);
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { document_d_exports } from "../../types/document.js";
|
|
2
|
+
|
|
3
|
+
//#region src/prosemirror/conversion/effectiveTableCellFormatting.d.ts
|
|
4
|
+
type TableCellMarginsAttrs = {
|
|
5
|
+
top?: number;
|
|
6
|
+
bottom?: number;
|
|
7
|
+
left?: number;
|
|
8
|
+
right?: number;
|
|
9
|
+
};
|
|
10
|
+
type EffectiveTableCellWidth = {
|
|
11
|
+
type: "none";
|
|
12
|
+
} | {
|
|
13
|
+
type: "value";
|
|
14
|
+
source: "direct" | "grid";
|
|
15
|
+
value: number;
|
|
16
|
+
widthType: document_d_exports.TableWidthType;
|
|
17
|
+
};
|
|
18
|
+
type EffectiveTableCellBackground = {
|
|
19
|
+
type: "none";
|
|
20
|
+
source: "none" | "direct" | "style";
|
|
21
|
+
} | {
|
|
22
|
+
type: "color";
|
|
23
|
+
source: "direct" | "style";
|
|
24
|
+
rgb: string;
|
|
25
|
+
};
|
|
26
|
+
type EffectiveTableCellFormatting = Readonly<{
|
|
27
|
+
width: EffectiveTableCellWidth;
|
|
28
|
+
background: EffectiveTableCellBackground;
|
|
29
|
+
borders: document_d_exports.TableCellBorders | undefined;
|
|
30
|
+
margins: TableCellMarginsAttrs | undefined;
|
|
31
|
+
}>;
|
|
32
|
+
type TableCellPosition = {
|
|
33
|
+
isFirstRow?: boolean;
|
|
34
|
+
isLastRow?: boolean;
|
|
35
|
+
isFirstColumn?: boolean;
|
|
36
|
+
isLastColumn?: boolean;
|
|
37
|
+
};
|
|
38
|
+
type ResolveEffectiveTableCellFormattingOptions = {
|
|
39
|
+
directFormatting: document_d_exports.TableCellFormatting | undefined;
|
|
40
|
+
styleFormatting: document_d_exports.TableCellFormatting | undefined;
|
|
41
|
+
tableBorders: document_d_exports.TableBorders | undefined;
|
|
42
|
+
position: TableCellPosition;
|
|
43
|
+
gridWidthPercent: number | undefined;
|
|
44
|
+
defaultMargins: TableCellMarginsAttrs | undefined;
|
|
45
|
+
theme: document_d_exports.Theme | null | undefined;
|
|
46
|
+
};
|
|
47
|
+
/**
|
|
48
|
+
* Resolve the display-facing table-cell cascade without mutating authored
|
|
49
|
+
* OOXML formatting. Source discriminators keep inherited values distinct from
|
|
50
|
+
* direct values that the editor may later override.
|
|
51
|
+
*/
|
|
52
|
+
declare const resolveEffectiveTableCellFormatting: ({
|
|
53
|
+
directFormatting,
|
|
54
|
+
styleFormatting,
|
|
55
|
+
tableBorders,
|
|
56
|
+
position,
|
|
57
|
+
gridWidthPercent,
|
|
58
|
+
defaultMargins,
|
|
59
|
+
theme
|
|
60
|
+
}: ResolveEffectiveTableCellFormattingOptions) => EffectiveTableCellFormatting;
|
|
61
|
+
//#endregion
|
|
62
|
+
export { TableCellMarginsAttrs, TableCellPosition, resolveEffectiveTableCellFormatting };
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import { resolveColor } from "../../utils/colorResolver.js";
|
|
2
|
+
import { resolveShadingFill } from "../../utils/formatToStyle.js";
|
|
3
|
+
//#region src/prosemirror/conversion/effectiveTableCellFormatting.ts
|
|
4
|
+
const TABLE_BORDER_SIDES = [
|
|
5
|
+
"top",
|
|
6
|
+
"bottom",
|
|
7
|
+
"left",
|
|
8
|
+
"right",
|
|
9
|
+
"insideH",
|
|
10
|
+
"insideV",
|
|
11
|
+
"topLeftToBottomRight",
|
|
12
|
+
"topRightToBottomLeft"
|
|
13
|
+
];
|
|
14
|
+
/**
|
|
15
|
+
* Resolve the display-facing table-cell cascade without mutating authored
|
|
16
|
+
* OOXML formatting. Source discriminators keep inherited values distinct from
|
|
17
|
+
* direct values that the editor may later override.
|
|
18
|
+
*/
|
|
19
|
+
const resolveEffectiveTableCellFormatting = ({ directFormatting, styleFormatting, tableBorders, position, gridWidthPercent, defaultMargins, theme }) => {
|
|
20
|
+
return {
|
|
21
|
+
width: resolveEffectiveWidth(directFormatting, gridWidthPercent),
|
|
22
|
+
background: resolveEffectiveBackground({
|
|
23
|
+
directFormatting,
|
|
24
|
+
styleFormatting,
|
|
25
|
+
theme
|
|
26
|
+
}),
|
|
27
|
+
borders: resolveEffectiveBorders({
|
|
28
|
+
directFormatting,
|
|
29
|
+
styleFormatting,
|
|
30
|
+
tableBorders,
|
|
31
|
+
position,
|
|
32
|
+
theme
|
|
33
|
+
}),
|
|
34
|
+
margins: resolveEffectiveMargins({
|
|
35
|
+
directFormatting,
|
|
36
|
+
styleFormatting,
|
|
37
|
+
defaultMargins
|
|
38
|
+
})
|
|
39
|
+
};
|
|
40
|
+
};
|
|
41
|
+
const resolveEffectiveWidth = (directFormatting, gridWidthPercent) => {
|
|
42
|
+
const directWidth = directFormatting?.width;
|
|
43
|
+
if (directWidth) return {
|
|
44
|
+
type: "value",
|
|
45
|
+
source: "direct",
|
|
46
|
+
value: directWidth.value,
|
|
47
|
+
widthType: directWidth.type
|
|
48
|
+
};
|
|
49
|
+
if (gridWidthPercent !== void 0) return {
|
|
50
|
+
type: "value",
|
|
51
|
+
source: "grid",
|
|
52
|
+
value: gridWidthPercent,
|
|
53
|
+
widthType: "pct"
|
|
54
|
+
};
|
|
55
|
+
return { type: "none" };
|
|
56
|
+
};
|
|
57
|
+
const resolveEffectiveBackground = ({ directFormatting, styleFormatting, theme }) => {
|
|
58
|
+
const directShading = directFormatting?.shading;
|
|
59
|
+
if (directShading !== void 0) return resolveBackgroundColor({
|
|
60
|
+
shading: directShading,
|
|
61
|
+
source: "direct",
|
|
62
|
+
theme
|
|
63
|
+
});
|
|
64
|
+
const styleShading = styleFormatting?.shading;
|
|
65
|
+
if (styleShading !== void 0) return resolveBackgroundColor({
|
|
66
|
+
shading: styleShading,
|
|
67
|
+
source: "style",
|
|
68
|
+
theme
|
|
69
|
+
});
|
|
70
|
+
return {
|
|
71
|
+
type: "none",
|
|
72
|
+
source: "none"
|
|
73
|
+
};
|
|
74
|
+
};
|
|
75
|
+
const resolveBackgroundColor = ({ shading, source, theme }) => {
|
|
76
|
+
const rgb = resolveShadingFill(shading, theme).replace(/^#/u, "");
|
|
77
|
+
if (!rgb) return {
|
|
78
|
+
type: "none",
|
|
79
|
+
source
|
|
80
|
+
};
|
|
81
|
+
return {
|
|
82
|
+
type: "color",
|
|
83
|
+
source,
|
|
84
|
+
rgb
|
|
85
|
+
};
|
|
86
|
+
};
|
|
87
|
+
const resolveEffectiveBorders = ({ directFormatting, styleFormatting, tableBorders, position, theme }) => {
|
|
88
|
+
const baseBorders = tableBorders ? {
|
|
89
|
+
top: position.isFirstRow ? tableBorders.top : tableBorders.insideH,
|
|
90
|
+
bottom: position.isLastRow ? tableBorders.bottom : tableBorders.insideH,
|
|
91
|
+
left: position.isFirstColumn ? tableBorders.left : tableBorders.insideV,
|
|
92
|
+
right: position.isLastColumn ? tableBorders.right : tableBorders.insideV
|
|
93
|
+
} : void 0;
|
|
94
|
+
const styleBorders = styleFormatting?.borders;
|
|
95
|
+
const directBorders = directFormatting?.borders;
|
|
96
|
+
const borders = baseBorders || styleBorders || directBorders ? {
|
|
97
|
+
...baseBorders,
|
|
98
|
+
...styleBorders,
|
|
99
|
+
...directBorders
|
|
100
|
+
} : void 0;
|
|
101
|
+
return resolveThemedBorderColors(borders, theme);
|
|
102
|
+
};
|
|
103
|
+
const resolveEffectiveMargins = ({ directFormatting, styleFormatting, defaultMargins }) => {
|
|
104
|
+
if (directFormatting?.margins) return cellMarginsToAttrs(directFormatting.margins);
|
|
105
|
+
if (styleFormatting?.margins) return cellMarginsToAttrs(styleFormatting.margins);
|
|
106
|
+
return defaultMargins;
|
|
107
|
+
};
|
|
108
|
+
const cellMarginsToAttrs = (margins) => {
|
|
109
|
+
const result = {};
|
|
110
|
+
if (margins.top?.value !== void 0) result.top = margins.top.value;
|
|
111
|
+
if (margins.bottom?.value !== void 0) result.bottom = margins.bottom.value;
|
|
112
|
+
if (margins.left?.value !== void 0) result.left = margins.left.value;
|
|
113
|
+
if (margins.right?.value !== void 0) result.right = margins.right.value;
|
|
114
|
+
return result;
|
|
115
|
+
};
|
|
116
|
+
const resolveThemedBorderColors = (borders, theme) => {
|
|
117
|
+
if (!borders || !theme?.colorScheme) return borders;
|
|
118
|
+
let resolved;
|
|
119
|
+
for (const side of TABLE_BORDER_SIDES) {
|
|
120
|
+
const border = borders[side];
|
|
121
|
+
if (!border?.color?.themeColor || border.color.auto) continue;
|
|
122
|
+
resolved ??= { ...borders };
|
|
123
|
+
resolved[side] = {
|
|
124
|
+
...border,
|
|
125
|
+
color: { rgb: resolveColor(border.color, theme).replace(/^#/u, "") }
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
return resolved ?? borders;
|
|
129
|
+
};
|
|
130
|
+
//#endregion
|
|
131
|
+
export { resolveEffectiveTableCellFormatting };
|
|
@@ -911,6 +911,7 @@ function createImageRun(node) {
|
|
|
911
911
|
if (attrs.opacity != null) image.opacity = attrs.opacity;
|
|
912
912
|
const imagePosition = imagePositionFromAttrs(attrs.position);
|
|
913
913
|
if (imagePosition) image.position = imagePosition;
|
|
914
|
+
if (attrs.layoutInCell !== void 0) image.layoutInCell = attrs.layoutInCell;
|
|
914
915
|
if (attrs.borderWidth && attrs.borderWidth > 0) {
|
|
915
916
|
const outline = {
|
|
916
917
|
width: pixelsToEmu(attrs.borderWidth),
|
|
@@ -7,10 +7,9 @@ import { assertValidProseMirrorDocument } from "../validation.js";
|
|
|
7
7
|
import { shadingToRunShadingAttrs } from "./runShadingMark.js";
|
|
8
8
|
import { marksToTextFormatting } from "./fromProseDoc.js";
|
|
9
9
|
import { createStyleEngine } from "../../style-engine/styleEngine.js";
|
|
10
|
-
import { resolveColor } from "../../utils/colorResolver.js";
|
|
11
|
-
import { resolveShadingFill } from "../../utils/formatToStyle.js";
|
|
12
10
|
import { buildRunFormattingOverrideAttrs } from "../extensions/marks/RunFormattingOverrideExtension.js";
|
|
13
11
|
import { schema } from "../schema/index.js";
|
|
12
|
+
import { resolveEffectiveTableCellFormatting } from "./effectiveTableCellFormatting.js";
|
|
14
13
|
//#region src/prosemirror/conversion/toProseDoc.ts
|
|
15
14
|
const TOC_STYLE_ID = /^TOC\d*$/iu;
|
|
16
15
|
/**
|
|
@@ -725,88 +724,60 @@ function convertTableRow(row, styleResolver, context, isHeaderRow, columnWidths,
|
|
|
725
724
|
if (cellIsFirstRow && cellIsLastCol && (tableLook?.firstRow || rowCnf?.firstRow || cellCnf?.firstRow) && (tableLook?.lastColumn || rowCnf?.lastColumn || cellCnf?.lastColumn)) cellConditionalStyle = mergeConditionalStyles(cellConditionalStyle, conditionalStyles?.neCell);
|
|
726
725
|
if (cellIsLastRow && cellIsFirstCol && (tableLook?.lastRow || rowCnf?.lastRow || cellCnf?.lastRow) && (tableLook?.firstColumn || rowCnf?.firstColumn || cellCnf?.firstColumn)) cellConditionalStyle = mergeConditionalStyles(cellConditionalStyle, conditionalStyles?.swCell);
|
|
727
726
|
if (cellIsLastRow && cellIsLastCol && (tableLook?.lastRow || rowCnf?.lastRow || cellCnf?.lastRow) && (tableLook?.lastColumn || rowCnf?.lastColumn || cellCnf?.lastColumn)) cellConditionalStyle = mergeConditionalStyles(cellConditionalStyle, conditionalStyles?.seCell);
|
|
728
|
-
cells.push(convertTableCell(
|
|
727
|
+
cells.push(convertTableCell({
|
|
728
|
+
cell,
|
|
729
|
+
styleResolver,
|
|
730
|
+
context,
|
|
731
|
+
isHeader: isHeaderRow,
|
|
732
|
+
gridWidthPercent: gridWidth,
|
|
733
|
+
conditionalStyle: cellConditionalStyle,
|
|
734
|
+
tableBorders,
|
|
735
|
+
position: {
|
|
736
|
+
isFirstRow,
|
|
737
|
+
isLastRow,
|
|
738
|
+
isFirstColumn: isFirstCol,
|
|
739
|
+
isLastColumn: isLastCol
|
|
740
|
+
},
|
|
741
|
+
calculatedRowSpan,
|
|
742
|
+
preserveVMergeRestart,
|
|
743
|
+
vMergeContinuationCells: rowSpanInfo?.continuationCells,
|
|
744
|
+
defaultCellMargins
|
|
745
|
+
}));
|
|
729
746
|
}
|
|
730
747
|
return schema.node("tableRow", attrs, cells);
|
|
731
748
|
}
|
|
732
|
-
const TABLE_BORDER_SIDES = [
|
|
733
|
-
"top",
|
|
734
|
-
"bottom",
|
|
735
|
-
"left",
|
|
736
|
-
"right",
|
|
737
|
-
"insideH",
|
|
738
|
-
"insideV",
|
|
739
|
-
"topLeftToBottomRight",
|
|
740
|
-
"topRightToBottomLeft"
|
|
741
|
-
];
|
|
742
|
-
function resolveThemedBorderColors(borders, theme) {
|
|
743
|
-
if (!borders || !theme?.colorScheme) return borders;
|
|
744
|
-
let resolved;
|
|
745
|
-
for (const side of TABLE_BORDER_SIDES) {
|
|
746
|
-
const border = borders[side];
|
|
747
|
-
if (!border?.color?.themeColor || border.color.auto) continue;
|
|
748
|
-
resolved ??= { ...borders };
|
|
749
|
-
resolved[side] = {
|
|
750
|
-
...border,
|
|
751
|
-
color: { rgb: resolveColor(border.color, theme).replace(/^#/u, "") }
|
|
752
|
-
};
|
|
753
|
-
}
|
|
754
|
-
return resolved ?? borders;
|
|
755
|
-
}
|
|
756
749
|
/**
|
|
757
750
|
* Convert a TableCell to a ProseMirror table cell node
|
|
758
751
|
*/
|
|
759
|
-
function convertTableCell(cell, styleResolver, context, isHeader, gridWidthPercent, conditionalStyle, tableBorders,
|
|
760
|
-
const { theme } = context;
|
|
752
|
+
function convertTableCell({ cell, styleResolver, context, isHeader, gridWidthPercent, conditionalStyle, tableBorders, position, calculatedRowSpan, preserveVMergeRestart, vMergeContinuationCells, defaultCellMargins }) {
|
|
761
753
|
const formatting = cell.formatting;
|
|
762
754
|
const rowspan = calculatedRowSpan ?? 1;
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
top: isFirstRow ? tableBorders.top : tableBorders.insideH,
|
|
773
|
-
bottom: isLastRow ? tableBorders.bottom : tableBorders.insideH,
|
|
774
|
-
left: isFirstCol ? tableBorders.left : tableBorders.insideV,
|
|
775
|
-
right: isLastCol ? tableBorders.right : tableBorders.insideV
|
|
776
|
-
};
|
|
777
|
-
})();
|
|
778
|
-
const conditionalBorders = conditionalStyle?.tcPr?.borders;
|
|
779
|
-
const cellBorders = formatting?.borders;
|
|
780
|
-
const borders = resolveThemedBorderColors(baseBorders || conditionalBorders || cellBorders ? {
|
|
781
|
-
...baseBorders,
|
|
782
|
-
...conditionalBorders,
|
|
783
|
-
...cellBorders
|
|
784
|
-
} : void 0, theme);
|
|
785
|
-
const buildMarginsAttr = (src) => {
|
|
786
|
-
const m = {};
|
|
787
|
-
if (src.top?.value !== void 0) m.top = src.top.value;
|
|
788
|
-
if (src.bottom?.value !== void 0) m.bottom = src.bottom.value;
|
|
789
|
-
if (src.left?.value !== void 0) m.left = src.left.value;
|
|
790
|
-
if (src.right?.value !== void 0) m.right = src.right.value;
|
|
791
|
-
return m;
|
|
792
|
-
};
|
|
755
|
+
const effectiveFormatting = resolveEffectiveTableCellFormatting({
|
|
756
|
+
directFormatting: formatting,
|
|
757
|
+
styleFormatting: conditionalStyle?.tcPr,
|
|
758
|
+
tableBorders,
|
|
759
|
+
position,
|
|
760
|
+
gridWidthPercent,
|
|
761
|
+
defaultMargins: defaultCellMargins,
|
|
762
|
+
theme: context.theme
|
|
763
|
+
});
|
|
793
764
|
const attrs = {
|
|
794
765
|
colspan: formatting?.gridSpan ?? 1,
|
|
795
766
|
rowspan
|
|
796
767
|
};
|
|
797
|
-
if (width
|
|
798
|
-
|
|
768
|
+
if (effectiveFormatting.width.type === "value") {
|
|
769
|
+
attrs.width = effectiveFormatting.width.value;
|
|
770
|
+
if (effectiveFormatting.width.widthType) attrs.widthType = effectiveFormatting.width.widthType;
|
|
771
|
+
}
|
|
799
772
|
if (formatting?.verticalAlign) attrs.verticalAlign = formatting.verticalAlign;
|
|
800
|
-
if (
|
|
801
|
-
attrs.backgroundColor =
|
|
802
|
-
attrs._resolvedBackgroundColor =
|
|
773
|
+
if (effectiveFormatting.background.type === "color") {
|
|
774
|
+
attrs.backgroundColor = effectiveFormatting.background.rgb;
|
|
775
|
+
attrs._resolvedBackgroundColor = effectiveFormatting.background.rgb;
|
|
803
776
|
}
|
|
804
777
|
if (formatting?.textDirection) attrs.textDirection = formatting.textDirection;
|
|
805
778
|
if (formatting?.noWrap !== void 0) attrs.noWrap = formatting.noWrap;
|
|
806
|
-
if (borders) attrs.borders = borders;
|
|
807
|
-
if (
|
|
808
|
-
else if (conditionalStyle?.tcPr?.margins) attrs.margins = buildMarginsAttr(conditionalStyle.tcPr.margins);
|
|
809
|
-
else if (defaultCellMargins) attrs.margins = defaultCellMargins;
|
|
779
|
+
if (effectiveFormatting.borders) attrs.borders = effectiveFormatting.borders;
|
|
780
|
+
if (effectiveFormatting.margins) attrs.margins = effectiveFormatting.margins;
|
|
810
781
|
if (formatting) attrs._originalFormatting = formatting;
|
|
811
782
|
if (cell.propertyChanges && cell.propertyChanges.length > 0) attrs.tcPrChange = [...cell.propertyChanges];
|
|
812
783
|
if (preserveVMergeRestart) attrs._preserveVMergeRestart = true;
|
|
@@ -1072,6 +1043,7 @@ function convertImage(image, rawXml) {
|
|
|
1072
1043
|
cropBottom: image.crop?.bottom,
|
|
1073
1044
|
cropLeft: image.crop?.left,
|
|
1074
1045
|
position,
|
|
1046
|
+
layoutInCell: image.layoutInCell,
|
|
1075
1047
|
borderWidth,
|
|
1076
1048
|
borderColor,
|
|
1077
1049
|
borderStyle,
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { autospacingMatchesBase } from "../../autospacingBase.js";
|
|
2
2
|
import { directionIsRtl } from "../../paragraphDirection.js";
|
|
3
3
|
import { expectParagraphAttrs } from "../../attrs/index.js";
|
|
4
|
-
import { paragraphToStyle } from "../../../utils/formatToStyle.js";
|
|
5
4
|
import { createNodeExtension } from "../create.js";
|
|
5
|
+
import { paragraphToStyle } from "../../../utils/formatToStyle.js";
|
|
6
6
|
import { collectHeadings } from "../../../utils/headingCollector.js";
|
|
7
7
|
import { listAttrsFromResolvedStyle, paragraphAttrsFromResolvedStyle } from "../../styles/resolvedStyleAttrs.js";
|
|
8
8
|
import { Fragment } from "prosemirror-model";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { HIGHLIGHT_COLOR_VALUES } from "../../../types/documentEnumValues.js";
|
|
2
2
|
import { expectHighlightMarkAttrs } from "../../attrs/index.js";
|
|
3
|
-
import { resolveHighlightToCss } from "../../../utils/colorResolver.js";
|
|
4
3
|
import { createMarkExtension } from "../create.js";
|
|
4
|
+
import { resolveHighlightToCss } from "../../../utils/colorResolver.js";
|
|
5
5
|
import { removeMark, setMark } from "./markUtils.js";
|
|
6
6
|
import { panic } from "better-result";
|
|
7
7
|
//#region src/prosemirror/extensions/marks/HighlightExtension.ts
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { expectRunShadingMarkAttrs } from "../../attrs/index.js";
|
|
2
|
-
import { ensureHexPrefix } from "../../../utils/colorResolver.js";
|
|
3
2
|
import { createMarkExtension } from "../create.js";
|
|
3
|
+
import { ensureHexPrefix } from "../../../utils/colorResolver.js";
|
|
4
4
|
import { normalizeCssColorKey, parseDOMHighlightColor } from "./HighlightExtension.js";
|
|
5
5
|
//#region src/prosemirror/extensions/marks/RunShadingExtension.ts
|
|
6
6
|
const RunShadingExtension = createMarkExtension({
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { expectTextColorMarkAttrs } from "../../attrs/index.js";
|
|
2
|
-
import { textToStyle } from "../../../utils/formatToStyle.js";
|
|
3
2
|
import { createMarkExtension } from "../create.js";
|
|
3
|
+
import { textToStyle } from "../../../utils/formatToStyle.js";
|
|
4
4
|
import { removeMark, setMark } from "./markUtils.js";
|
|
5
5
|
import { panic } from "better-result";
|
|
6
6
|
//#region src/prosemirror/extensions/marks/TextColorExtension.ts
|
|
@@ -33,6 +33,7 @@ const ImageExtension = createNodeExtension({
|
|
|
33
33
|
cropBottom: { default: null },
|
|
34
34
|
cropLeft: { default: null },
|
|
35
35
|
position: { default: null },
|
|
36
|
+
layoutInCell: { default: null },
|
|
36
37
|
borderWidth: { default: null },
|
|
37
38
|
borderColor: { default: null },
|
|
38
39
|
borderStyle: { default: null },
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { TABLE_CELL_TEXT_DIRECTION_VALUES, TABLE_WIDTH_TYPE_VALUES } from "../../../types/documentEnumValues.js";
|
|
2
2
|
import { expectTableAttrs, expectTableCellAttrs, expectTableRowAttrs, mergeTableAttrs, mergeTableCellAttrs, mergeTableRowAttrs } from "../../attrs/index.js";
|
|
3
|
-
import { resolveColor } from "../../../utils/colorResolver.js";
|
|
4
3
|
import { createExtension, createNodeExtension } from "../create.js";
|
|
4
|
+
import { resolveColor } from "../../../utils/colorResolver.js";
|
|
5
5
|
import { panic } from "better-result";
|
|
6
6
|
import { Plugin, PluginKey, Selection, TextSelection } from "prosemirror-state";
|
|
7
7
|
import { CellSelection, TableMap, columnResizing, mergeCells, removeRow, selectedRect, splitCell, tableEditing } from "prosemirror-tables";
|
|
@@ -197,7 +197,8 @@ type ImageAttrs = {
|
|
|
197
197
|
cropRight?: number;
|
|
198
198
|
cropBottom?: number;
|
|
199
199
|
cropLeft?: number; /** Position for floating images (horizontal and vertical alignment) */
|
|
200
|
-
position?: ImagePositionAttrs; /**
|
|
200
|
+
position?: ImagePositionAttrs; /** Use the containing table cell as the anchor's positioning scope (the OOXML default). */
|
|
201
|
+
layoutInCell?: boolean; /** Border width in pixels */
|
|
201
202
|
borderWidth?: number; /** Border color as CSS color string */
|
|
202
203
|
borderColor?: string; /** Border style (CSS border-style value) */
|
|
203
204
|
borderStyle?: string; /** Wrap text setting from DOCX (left, right, bothSides, largest) for round-trip */
|
package/dist/redline.d.ts
CHANGED
|
@@ -1,20 +1,35 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { FolioAIEditAppliedOperation, FolioAIEditSkippedOperation } from "./ai-edits/types.js";
|
|
2
|
+
import { FolioDocumentStoryHandle, FolioResolvedReviewedView } from "./ai-edits/headless.js";
|
|
3
|
+
import { FolioDocumentPrivacyOptions, FolioDocumentPrivacyReport } from "./docx/metadataPrivacy.js";
|
|
2
4
|
|
|
3
5
|
//#region src/redline.d.ts
|
|
4
6
|
/** Options for {@link generateRedlineDocx}. */
|
|
5
7
|
type GenerateRedlineDocxOptions = {
|
|
6
|
-
/** Author recorded on the generated tracked changes. (default: `"folio compare"`) */author?: string;
|
|
8
|
+
/** Author recorded on the generated tracked changes. (default: `"folio compare"`) */author?: string; /** Resolved base input state. (default: `"final"`) */
|
|
9
|
+
baseView?: FolioResolvedReviewedView; /** Resolved revised input state. (default: `"final"`) */
|
|
10
|
+
revisedView?: FolioResolvedReviewedView; /** Optional output-only package-metadata privacy transforms. */
|
|
11
|
+
privacy?: FolioDocumentPrivacyOptions;
|
|
12
|
+
};
|
|
13
|
+
declare const InvalidGenerateRedlineDocxOptionsError_base: import("better-result").TaggedErrorClass<"InvalidGenerateRedlineDocxOptionsError", {
|
|
14
|
+
message: string;
|
|
15
|
+
option: "baseView" | "revisedView";
|
|
16
|
+
receivedValue: unknown;
|
|
17
|
+
}>;
|
|
18
|
+
declare class InvalidGenerateRedlineDocxOptionsError extends InvalidGenerateRedlineDocxOptionsError_base {}
|
|
19
|
+
type GenerateRedlineUnprocessedStory = {
|
|
20
|
+
baseStory: FolioDocumentStoryHandle | null;
|
|
21
|
+
revisedStory: FolioDocumentStoryHandle | null;
|
|
22
|
+
reason: "missing-base-story" | "missing-revised-story";
|
|
7
23
|
};
|
|
8
24
|
/** Result of {@link generateRedlineDocx}. */
|
|
9
|
-
type GenerateRedlineDocxResult =
|
|
10
|
-
/** The
|
|
25
|
+
type GenerateRedlineDocxResult = {
|
|
26
|
+
/** The base package with generated tracked changes. */buffer: ArrayBuffer; /** Operations applied across every matched story. */
|
|
27
|
+
applied: FolioAIEditAppliedOperation[]; /** Block operations that could not be applied. */
|
|
28
|
+
skipped: FolioAIEditSkippedOperation[]; /** Package parts that could not be represented as story-scoped text edits. */
|
|
29
|
+
unprocessedStories: GenerateRedlineUnprocessedStory[]; /** Privacy transforms applied to the generated package. */
|
|
30
|
+
privacyReport: FolioDocumentPrivacyReport;
|
|
11
31
|
};
|
|
12
|
-
/**
|
|
13
|
-
* Compare `base` and `revised` and return the base document with every
|
|
14
|
-
* difference recorded as a tracked change. See the module doc comment for
|
|
15
|
-
* semantics and limitations; `skipped` reports any block the generator could
|
|
16
|
-
* not redline (e.g. additions with no anchor in an empty base document).
|
|
17
|
-
*/
|
|
32
|
+
/** Compare two buffers and return tracked changes for every matched editable story. */
|
|
18
33
|
declare const generateRedlineDocx: (base: ArrayBuffer, revised: ArrayBuffer, options?: GenerateRedlineDocxOptions) => Promise<GenerateRedlineDocxResult>;
|
|
19
34
|
//#endregion
|
|
20
|
-
export { GenerateRedlineDocxOptions, GenerateRedlineDocxResult, generateRedlineDocx };
|
|
35
|
+
export { GenerateRedlineDocxOptions, GenerateRedlineDocxResult, GenerateRedlineUnprocessedStory, InvalidGenerateRedlineDocxOptionsError, generateRedlineDocx };
|