@stll/folio-core 0.57.0 → 0.58.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/apply.js +77 -8
- package/dist/controller/canonicalClipboard.d.ts +3 -1
- package/dist/controller/canonicalClipboard.js +24 -12
- package/dist/controller/canonicalComposition.d.ts +13 -4
- package/dist/controller/canonicalComposition.js +82 -9
- package/dist/controller/canonicalInput.d.ts +1 -1
- package/dist/controller/canonicalInput.js +4 -3
- package/dist/controller/canonicalSelection.js +10 -1
- package/dist/controller/canonicalSession.d.ts +1 -1
- package/dist/controller/canonicalSession.js +59 -23
- package/dist/controller/canonicalStoryEditor.d.ts +1 -1
- package/dist/controller/canonicalStoryEditor.js +1 -1
- package/dist/controller/canonicalStructure.js +8 -3
- package/dist/controller/hiddenEditorApi.js +1 -1
- package/dist/controller/hiddenEditorManager.js +1 -1
- package/dist/docx/listRendering.d.ts +18 -0
- package/dist/{prosemirror/conversion/listRenderingDefinition.js → docx/listRendering.js} +17 -3
- package/dist/docx/server/materializeYjsDocx.js +1 -1
- package/dist/markdown/internals.js +7 -0
- package/dist/markdown/renderParagraph.js +11 -12
- package/dist/markdown/renderRuns.js +2 -2
- package/dist/markdown/types.d.ts +3 -0
- package/dist/prosemirror/attrs/index.js +4 -0
- package/dist/prosemirror/conversion/fromProseDoc.d.ts +8 -1
- package/dist/prosemirror/conversion/fromProseDoc.js +128 -27
- package/dist/prosemirror/conversion/toProseDoc.js +10 -23
- package/dist/prosemirror/extensions/nodes/ImageExtension.js +8 -1
- package/dist/prosemirror/imagePictureNames.d.ts +12 -0
- package/dist/prosemirror/imagePictureNames.js +20 -0
- package/dist/prosemirror/paragraphFormattingProvenance.js +1 -0
- package/dist/prosemirror/runStyleFormatting.d.ts +72 -2
- package/dist/prosemirror/runStyleFormatting.js +18 -1
- package/dist/prosemirror/schema/nodes.d.ts +1 -0
- package/dist/prosemirror/styles/styleResolver.d.ts +4 -1
- package/dist/prosemirror/styles/styleResolver.js +32 -11
- package/dist/types/canonicalCapabilities.d.ts +15 -1
- package/dist/types/canonicalCapabilities.js +15 -1
- package/package.json +2 -2
- package/dist/prosemirror/conversion/listRenderingDefinition.d.ts +0 -6
|
@@ -12,8 +12,8 @@ import { mapTocSelection } from "./canonicalTocSelection.js";
|
|
|
12
12
|
import { Result, TaggedError, panic } from "better-result";
|
|
13
13
|
import { hasIllegalXmlCharacters } from "@stll/docx-core";
|
|
14
14
|
import { Fragment } from "prosemirror-model";
|
|
15
|
-
import { DOCUMENT_OP_TYPES, OP_STORIES, applyDocumentOps, combineEdits, commentDocumentIssue, compileEditorIntent, createEditorIntentIdAllocator, documentStories, editorParagraphGroups, findStoryBody, normalizeForOps, packageParagraphIds, sameStory, validateOpsDocument } from "@stll/docx-core/ops";
|
|
16
|
-
import { AllSelection, TextSelection } from "prosemirror-state";
|
|
15
|
+
import { DOCUMENT_OP_REFUSAL_REASONS, DOCUMENT_OP_TYPES, OP_STORIES, applyDocumentOps, combineEdits, commentDocumentIssue, compileEditorIntent, createEditorIntentIdAllocator, documentStories, editorParagraphGroups, findStoryBody, normalizeForOps, packageParagraphIds, sameStory, validateOpsDocument } from "@stll/docx-core/ops";
|
|
16
|
+
import { AllSelection, NodeSelection, TextSelection } from "prosemirror-state";
|
|
17
17
|
//#region src/controller/canonicalSession.ts
|
|
18
18
|
var CanonicalSessionError = class extends TaggedError("CanonicalSessionError") {};
|
|
19
19
|
const refuse = (message) => Result.err(new CanonicalSessionError({
|
|
@@ -183,12 +183,21 @@ var CanonicalProjection = class {
|
|
|
183
183
|
return Result.ok(paragraph.start + gap.position);
|
|
184
184
|
}
|
|
185
185
|
selectionAt(state) {
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
186
|
+
const selection = state.selection;
|
|
187
|
+
let type;
|
|
188
|
+
let anchorPosition = selection.anchor;
|
|
189
|
+
let headPosition = selection.head;
|
|
190
|
+
if (selection instanceof TextSelection) type = "text";
|
|
191
|
+
else if (selection instanceof NodeSelection && selection.node.isInline && selection.node.isAtom && selection.node.type.name !== "noteMarker") type = "inlineNode";
|
|
192
|
+
else if (selection instanceof AllSelection) {
|
|
193
|
+
type = "all";
|
|
194
|
+
const range = canonicalSelectionRange(state);
|
|
195
|
+
anchorPosition = range.from;
|
|
196
|
+
headPosition = range.to;
|
|
197
|
+
} else return refuse("Canonical input requires text, an inline atom, or the whole document selection.");
|
|
198
|
+
const anchor = this.addressAt(anchorPosition);
|
|
190
199
|
if (anchor.isErr()) return anchor;
|
|
191
|
-
const head = this.addressAt(
|
|
200
|
+
const head = this.addressAt(headPosition);
|
|
192
201
|
if (head.isErr()) return head;
|
|
193
202
|
return Result.ok({
|
|
194
203
|
type,
|
|
@@ -294,6 +303,7 @@ const project = ({ document, styles, story = OP_STORIES.MAIN }) => {
|
|
|
294
303
|
const paragraphs = [];
|
|
295
304
|
let failure;
|
|
296
305
|
converted.value.forEach((node, offset, index) => {
|
|
306
|
+
if (failure !== void 0) return;
|
|
297
307
|
const source = body.content.at(index);
|
|
298
308
|
if (source?.type !== "paragraph" || source.paraId === void 0) {
|
|
299
309
|
failure = new CanonicalSessionError({
|
|
@@ -305,7 +315,7 @@ const project = ({ document, styles, story = OP_STORIES.MAIN }) => {
|
|
|
305
315
|
}
|
|
306
316
|
if (node.type.name !== "paragraph" || node.attrs["paraId"] !== source.paraId) {
|
|
307
317
|
failure = new CanonicalSessionError({
|
|
308
|
-
gap: CANONICAL_GAP.
|
|
318
|
+
gap: CANONICAL_GAP.storyContentProjection,
|
|
309
319
|
reason: "refused",
|
|
310
320
|
message: "The projection changed paragraph structure."
|
|
311
321
|
});
|
|
@@ -595,9 +605,13 @@ var CanonicalSession = class {
|
|
|
595
605
|
if (!Number.isFinite(time)) return refuse("The input time is invalid.");
|
|
596
606
|
if (from > to || hasIllegalXmlCharacters(text) || /[\t\r\n]/u.test(text)) return refuse("Canonical input accepts a well-formed text replacement.");
|
|
597
607
|
if (from === to && text.length === 0) return noChange("The input makes no text change.");
|
|
598
|
-
const
|
|
608
|
+
const replacesAll = state.selection instanceof AllSelection && from === state.selection.from && to === state.selection.to;
|
|
609
|
+
const range = canonicalSelectionRange(state);
|
|
610
|
+
const inputFrom = replacesAll ? range.from : from;
|
|
611
|
+
const inputTo = replacesAll ? range.to : to;
|
|
612
|
+
const start = projection.inputAddressAt(inputFrom);
|
|
599
613
|
if (start.isErr()) return start;
|
|
600
|
-
const end = projection.inputAddressAt(
|
|
614
|
+
const end = projection.inputAddressAt(inputTo);
|
|
601
615
|
if (end.isErr()) return end;
|
|
602
616
|
const preSelection = projection.selectionAt(state);
|
|
603
617
|
if (preSelection.isErr()) return preSelection;
|
|
@@ -695,7 +709,14 @@ var CanonicalSession = class {
|
|
|
695
709
|
mode: this.intentMode(document, intent),
|
|
696
710
|
firstBlockId: this.nextBlockId
|
|
697
711
|
});
|
|
698
|
-
if (compiled.isErr())
|
|
712
|
+
if (compiled.isErr()) {
|
|
713
|
+
if (compiled.error.reason === DOCUMENT_OP_REFUSAL_REASONS.UNTRACKABLE && (intent.type === "setHyperlink" || intent.type === "removeHyperlink" || intent.type === "insertHyperlink")) return Result.err(new CanonicalSessionError({
|
|
714
|
+
gap: CANONICAL_GAP.trackedHyperlinkResolution,
|
|
715
|
+
reason: "refused",
|
|
716
|
+
message: compiled.error.message
|
|
717
|
+
}));
|
|
718
|
+
return refuse(compiled.error.message);
|
|
719
|
+
}
|
|
699
720
|
const applied = applyDocumentOps(document, compiled.value.ops);
|
|
700
721
|
if (applied.isErr()) return refuse(applied.error.message);
|
|
701
722
|
document = applied.value.document;
|
|
@@ -981,8 +1002,11 @@ var CanonicalSession = class {
|
|
|
981
1002
|
document: applied.value.document,
|
|
982
1003
|
previous: this.currentDocument
|
|
983
1004
|
});
|
|
984
|
-
|
|
985
|
-
|
|
1005
|
+
if (unsupportedSeedReason(applied.value.document, changed) !== null) return Result.err(new CanonicalSessionError({
|
|
1006
|
+
gap: CANONICAL_GAP.storyContentProjection,
|
|
1007
|
+
reason: "refused",
|
|
1008
|
+
message: "The operations produce unsupported canonical story content."
|
|
1009
|
+
}));
|
|
986
1010
|
preservePropertySources({
|
|
987
1011
|
target: applied.value.document,
|
|
988
1012
|
source: this.currentDocument,
|
|
@@ -1037,15 +1061,17 @@ var CanonicalSession = class {
|
|
|
1037
1061
|
transaction.replaceWith(from, to, Fragment.fromArray(replacement));
|
|
1038
1062
|
}
|
|
1039
1063
|
if (!transaction.doc.eq(projected.value.doc)) transaction.replaceWith(0, transaction.doc.content.size, projected.value.doc.content);
|
|
1040
|
-
if (sameStory(selection.anchor.story, projectedStory) && sameStory(selection.head.story, projectedStory))
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1064
|
+
if (sameStory(selection.anchor.story, projectedStory) && sameStory(selection.head.story, projectedStory)) {
|
|
1065
|
+
if (!restoreCanonicalSelection({
|
|
1066
|
+
transaction,
|
|
1067
|
+
projection: projected.value,
|
|
1068
|
+
selection,
|
|
1069
|
+
unavailable: {
|
|
1070
|
+
type: "near",
|
|
1071
|
+
anchor: state.selection.anchor
|
|
1072
|
+
}
|
|
1073
|
+
})) return refuse("Canonical history cannot restore the selected inline atom.");
|
|
1074
|
+
}
|
|
1049
1075
|
transaction.setMeta(CANONICAL_PROJECTION_META, {
|
|
1050
1076
|
type: "canonical",
|
|
1051
1077
|
origin,
|
|
@@ -1104,6 +1130,11 @@ var CanonicalSession = class {
|
|
|
1104
1130
|
}
|
|
1105
1131
|
};
|
|
1106
1132
|
const createCanonicalSession = (document, styles) => {
|
|
1133
|
+
if (documentStories(document).some((story) => findStoryBody(document, story)?.content.some((block) => block.type === "table"))) return Result.err(new CanonicalSessionError({
|
|
1134
|
+
gap: CANONICAL_GAP.tableActivation,
|
|
1135
|
+
reason: "refused",
|
|
1136
|
+
message: "Canonical sessions cannot activate documents containing tables."
|
|
1137
|
+
}));
|
|
1107
1138
|
const unsupported = unsupportedSeedReason(document);
|
|
1108
1139
|
if (unsupported !== null) return refuse(unsupported);
|
|
1109
1140
|
const owned = cloneDocumentWithParagraphPropertySources(document);
|
|
@@ -1192,7 +1223,12 @@ const isCanonicalJoinBoundary = (state, direction) => {
|
|
|
1192
1223
|
/** Native character deletion consumes one whole visible grapheme. */
|
|
1193
1224
|
const deletionRange = (state, direction) => {
|
|
1194
1225
|
const { selection } = state;
|
|
1195
|
-
if (
|
|
1226
|
+
if (selection instanceof NodeSelection && selection.node.isInline && selection.node.isAtom && selection.node.type.name !== "noteMarker") return Result.ok({
|
|
1227
|
+
from: selection.from,
|
|
1228
|
+
to: selection.to
|
|
1229
|
+
});
|
|
1230
|
+
if (selection instanceof AllSelection) return Result.ok(canonicalSelectionRange(state));
|
|
1231
|
+
if (!(selection instanceof TextSelection)) return refuse("Canonical deletion requires text, an inline atom, or the whole document selection.");
|
|
1196
1232
|
if (!selection.empty) return Result.ok({
|
|
1197
1233
|
from: selection.from,
|
|
1198
1234
|
to: selection.to
|
|
@@ -25,7 +25,7 @@ declare const createCanonicalStoryEditor: ({ story, getView, getApi, enabled, on
|
|
|
25
25
|
beforeinput: (view: EditorView, event: InputEvent) => boolean;
|
|
26
26
|
mousedown: (view: EditorView) => boolean;
|
|
27
27
|
compositionstart: (view: EditorView) => boolean;
|
|
28
|
-
compositionend: (view: EditorView) => boolean;
|
|
28
|
+
compositionend: (view: EditorView, event: Event) => boolean;
|
|
29
29
|
input: (view: EditorView) => boolean;
|
|
30
30
|
blur: (view: EditorView) => boolean;
|
|
31
31
|
paste: (view: EditorView, event: ClipboardEvent) => boolean;
|
|
@@ -93,7 +93,7 @@ const createCanonicalStoryEditor = ({ story, getView, getApi, enabled, onRefusal
|
|
|
93
93
|
beforeinput: (view, event) => enabled() && boundary.handleDOMEvents.beforeinput(view, event),
|
|
94
94
|
mousedown: (view) => enabled() && boundary.handleDOMEvents.mousedown(view),
|
|
95
95
|
compositionstart: (view) => enabled() && boundary.handleDOMEvents.compositionstart(view),
|
|
96
|
-
compositionend: (view) => enabled() && boundary.handleDOMEvents.compositionend(view),
|
|
96
|
+
compositionend: (view, event) => enabled() && boundary.handleDOMEvents.compositionend(view, event),
|
|
97
97
|
input: (view) => enabled() && boundary.handleDOMEvents.input(view),
|
|
98
98
|
blur: (view) => enabled() && boundary.handleDOMEvents.blur(view),
|
|
99
99
|
paste: (view, event) => enabled() && boundary.handleDOMEvents.paste(view, event),
|
|
@@ -15,6 +15,11 @@ const refuse = (message) => Result.err(new CanonicalSessionError({
|
|
|
15
15
|
message,
|
|
16
16
|
reason: "refused"
|
|
17
17
|
}));
|
|
18
|
+
const noChange = (message) => Result.err(new CanonicalSessionError({
|
|
19
|
+
gap: CANONICAL_GAP.commands,
|
|
20
|
+
message,
|
|
21
|
+
reason: "noChange"
|
|
22
|
+
}));
|
|
18
23
|
const selectedParagraphs = (session, state) => {
|
|
19
24
|
const ids = /* @__PURE__ */ new Set();
|
|
20
25
|
state.doc.nodesBetween(state.selection.from, state.selection.to, (node) => {
|
|
@@ -218,7 +223,7 @@ const prepareCanonicalCommands = (session, state, commands) => {
|
|
|
218
223
|
offset: 0
|
|
219
224
|
};
|
|
220
225
|
const level = (numPr.ilvl ?? 0) + (command.direction === "increase" ? 1 : -1);
|
|
221
|
-
if (level > 8) return
|
|
226
|
+
if (level > 8) return noChange("The list is already at its deepest level.");
|
|
222
227
|
intents.push({
|
|
223
228
|
type: "formatParagraph",
|
|
224
229
|
at,
|
|
@@ -234,7 +239,7 @@ const prepareCanonicalCommands = (session, state, commands) => {
|
|
|
234
239
|
const source = paragraphs.at(0);
|
|
235
240
|
const numPr = source?.formatting?.numPr;
|
|
236
241
|
const definitions = session.document.package.numbering;
|
|
237
|
-
if (source === void 0 || numPr?.kind !== "reference" || definitions === void 0) return
|
|
242
|
+
if (source === void 0 || numPr?.kind !== "reference" || definitions === void 0) return noChange("Numbering changes require a list paragraph.");
|
|
238
243
|
const body = session.document.package.document.content;
|
|
239
244
|
const index = body.findIndex((item) => item === source);
|
|
240
245
|
const affected = body.slice(index).filter((item) => item.type === "paragraph" && item.formatting?.numPr?.kind === "reference" && item.formatting.numPr.numId === numPr.numId);
|
|
@@ -244,7 +249,7 @@ const prepareCanonicalCommands = (session, state, commands) => {
|
|
|
244
249
|
const map = getCachedNumberingMap(definitions);
|
|
245
250
|
const bullet = numberingLevelUsesBulletMarker(map.getLevel(numPr.numId, numPr.ilvl ?? 0));
|
|
246
251
|
const earlier = body.slice(0, index).findLast((item) => item.type === "paragraph" && item.formatting?.numPr?.kind === "reference" && item.formatting.numPr.numId !== numPr.numId && numberingLevelUsesBulletMarker(map.getLevel(item.formatting.numPr.numId, item.formatting.numPr.ilvl ?? 0)) === bullet);
|
|
247
|
-
if (earlier?.type !== "paragraph" || earlier.formatting?.numPr?.kind !== "reference") return
|
|
252
|
+
if (earlier?.type !== "paragraph" || earlier.formatting?.numPr?.kind !== "reference") return noChange("There is no preceding compatible list.");
|
|
248
253
|
targetId = earlier.formatting.numPr.numId;
|
|
249
254
|
target = {
|
|
250
255
|
type: "existing",
|
|
@@ -9,7 +9,7 @@ import { redo, undo } from "prosemirror-history";
|
|
|
9
9
|
*/
|
|
10
10
|
const stateToDocument = (state, originalDoc) => {
|
|
11
11
|
if (!originalDoc) return null;
|
|
12
|
-
return fromProseDoc(state.doc, originalDoc);
|
|
12
|
+
return fromProseDoc(state.doc, originalDoc, { stylesheetSource: { type: "package" } });
|
|
13
13
|
};
|
|
14
14
|
const createHiddenEditorApi = (deps) => {
|
|
15
15
|
const dispatchTr = (view, tr) => {
|
|
@@ -688,7 +688,7 @@ const createHiddenEditorManager = (deps) => {
|
|
|
688
688
|
blur: (pmView) => editorSession.type === "canonical" ? input.handleDOMEvents.blur(pmView) : false,
|
|
689
689
|
...createHiddenEditorClipboardHandlers(deps),
|
|
690
690
|
compositionstart: (pmView) => editorSession.type === "canonical" ? input.handleDOMEvents.compositionstart(pmView) : false,
|
|
691
|
-
compositionend: (pmView) => editorSession.type === "canonical" ? input.handleDOMEvents.compositionend(pmView) : false,
|
|
691
|
+
compositionend: (pmView, event) => editorSession.type === "canonical" ? input.handleDOMEvents.compositionend(pmView, event) : false,
|
|
692
692
|
input: (pmView) => editorSession.type === "canonical" ? input.handleDOMEvents.input(pmView) : false,
|
|
693
693
|
paste: (pmView, event) => {
|
|
694
694
|
if (editorSession.type === "canonical" && !deps.getReadOnly()) return input.handleDOMEvents.paste(pmView, event);
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { document_d_exports } from "../types/document.js";
|
|
2
|
+
import { NumberingMap } from "./numberingParser.js";
|
|
3
|
+
//#region src/docx/listRendering.d.ts
|
|
4
|
+
/** Compare scalar fields directly; only nested records need canonical ordering. */
|
|
5
|
+
declare const listRenderingDefinitionsMatch: (left: document_d_exports.ListRendering, right: document_d_exports.ListRendering) => boolean;
|
|
6
|
+
type ListRenderingDefinition = {
|
|
7
|
+
rendering: document_d_exports.ListRendering | null;
|
|
8
|
+
nextSlotOffset: number | undefined;
|
|
9
|
+
};
|
|
10
|
+
/** Resolve numbering facts independently of any paragraph's cached counters. */
|
|
11
|
+
declare const resolveListRenderingDefinition: (numPr: {
|
|
12
|
+
numId: number;
|
|
13
|
+
ilvl?: number;
|
|
14
|
+
}, numbering: NumberingMap | undefined) => ListRenderingDefinition;
|
|
15
|
+
/** Keep content-derived counter/folded-field facts only while their definition is current. */
|
|
16
|
+
declare const resolveCachedListRendering: (cached: document_d_exports.ListRendering | undefined, { rendering, nextSlotOffset }: ListRenderingDefinition) => document_d_exports.ListRendering | null;
|
|
17
|
+
//#endregion
|
|
18
|
+
export { ListRenderingDefinition, listRenderingDefinitionsMatch, resolveCachedListRendering, resolveListRenderingDefinition };
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { canonicalJson } from "
|
|
2
|
-
|
|
1
|
+
import { canonicalJson } from "../utils/canonicalJson.js";
|
|
2
|
+
import { computeListRendering } from "./numberingParser.js";
|
|
3
|
+
//#region src/docx/listRendering.ts
|
|
3
4
|
const DEFINITION_FIELDS = Object.values({
|
|
4
5
|
marker: {
|
|
5
6
|
key: "marker",
|
|
@@ -93,5 +94,18 @@ const listRenderingDefinitionsMatch = (left, right) => {
|
|
|
93
94
|
}
|
|
94
95
|
return true;
|
|
95
96
|
};
|
|
97
|
+
/** Resolve numbering facts independently of any paragraph's cached counters. */
|
|
98
|
+
const resolveListRenderingDefinition = (numPr, numbering) => {
|
|
99
|
+
const nextLevel = numbering?.getLevel(numPr.numId, (numPr.ilvl ?? 0) + 1);
|
|
100
|
+
return {
|
|
101
|
+
rendering: numbering === void 0 ? null : computeListRendering(numPr, numbering),
|
|
102
|
+
nextSlotOffset: nextLevel?.pPr?.hangingIndent === true && nextLevel.pPr.indentFirstLine !== void 0 && nextLevel.pPr.indentFirstLine < 0 ? -nextLevel.pPr.indentFirstLine : void 0
|
|
103
|
+
};
|
|
104
|
+
};
|
|
105
|
+
/** Keep content-derived counter/folded-field facts only while their definition is current. */
|
|
106
|
+
const resolveCachedListRendering = (cached, { rendering, nextSlotOffset }) => {
|
|
107
|
+
if (rendering === null) return null;
|
|
108
|
+
return cached !== void 0 && listRenderingDefinitionsMatch(cached, rendering) && (cached.markerSecondSlotOffsetTwips === void 0 || cached.markerSecondSlotOffsetTwips === nextSlotOffset) ? cached : rendering;
|
|
109
|
+
};
|
|
96
110
|
//#endregion
|
|
97
|
-
export { listRenderingDefinitionsMatch };
|
|
111
|
+
export { listRenderingDefinitionsMatch, resolveCachedListRendering, resolveListRenderingDefinition };
|
|
@@ -82,7 +82,7 @@ const materializeYjsDocx = async ({ sourceDocx, yjsUpdate }) => {
|
|
|
82
82
|
const proseMirrorDocument = readProseMirrorDocument(yjsUpdate);
|
|
83
83
|
const baseDocument = await parseDocx(sourceDocx, { preloadFonts: false });
|
|
84
84
|
const converted = Result.try({
|
|
85
|
-
try: () => fromProseDoc(proseMirrorDocument, baseDocument),
|
|
85
|
+
try: () => fromProseDoc(proseMirrorDocument, baseDocument, { stylesheetSource: { type: "package" } }),
|
|
86
86
|
catch: (cause) => cause instanceof ParagraphPropertySourceValidationError ? new FolioYjsDocxMaterializationError({
|
|
87
87
|
code: "source_mismatch",
|
|
88
88
|
message: "The collaboration snapshot belongs to a different source document.",
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { createBuiltInStyleIndex } from "../docx/builtInStyles.js";
|
|
2
2
|
import { createListLabelCounter } from "../prosemirror/listLabels.js";
|
|
3
|
+
import { createStyleResolver, resolveStyleParagraphNumbering } from "../prosemirror/styles/styleResolver.js";
|
|
3
4
|
import { createNoteReferenceNumbering, noteReferenceMarker, noteReferenceToken } from "../utils/noteReferenceLabels.js";
|
|
4
5
|
//#region src/markdown/internals.ts
|
|
5
6
|
/**
|
|
@@ -18,7 +19,13 @@ import { createNoteReferenceNumbering, noteReferenceMarker, noteReferenceToken }
|
|
|
18
19
|
* make the render quadratic.
|
|
19
20
|
*/
|
|
20
21
|
function newContext(opts = {}, styles) {
|
|
22
|
+
const resolver = createStyleResolver(styles);
|
|
23
|
+
const numbering = /* @__PURE__ */ new Map();
|
|
21
24
|
return {
|
|
25
|
+
paragraphNumbering: (styleId) => {
|
|
26
|
+
if (!numbering.has(styleId)) numbering.set(styleId, resolveStyleParagraphNumbering(resolver, styleId));
|
|
27
|
+
return numbering.get(styleId);
|
|
28
|
+
},
|
|
22
29
|
builtInStyles: createBuiltInStyleIndex(styles?.styles ?? [], styles?.docDefaults),
|
|
23
30
|
opts: {
|
|
24
31
|
annotations: opts.annotations ?? "html",
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import { isQuoteStyle, resolveHeadingLevel } from "../docx/builtInStyles.js";
|
|
2
|
+
import { resolveCachedListRendering, resolveListRenderingDefinition } from "../docx/listRendering.js";
|
|
3
|
+
import { getCachedNumberingMap } from "../docx/numberingParser.js";
|
|
4
|
+
import { mergeParagraphNumbering } from "../docx/numberingReference.js";
|
|
2
5
|
import { listLabelAttrsFromRendering } from "../prosemirror/listLabels.js";
|
|
3
6
|
import { inlineBreaksToHtml } from "./escape.js";
|
|
4
7
|
import { renderParagraphInline } from "./renderRuns.js";
|
|
@@ -32,7 +35,12 @@ function renderParagraph(ctx, pkg, para) {
|
|
|
32
35
|
* does not define as prose, as every other reader reads them.
|
|
33
36
|
*/
|
|
34
37
|
function renderParagraphBlock(ctx, pkg, para) {
|
|
35
|
-
const
|
|
38
|
+
const numPrFromStyle = ctx.paragraphNumbering(para.formatting?.styleId) ?? para.formatting?.numPrFromStyle;
|
|
39
|
+
const numPr = mergeParagraphNumbering(numPrFromStyle, para.formatting?.numPr);
|
|
40
|
+
let list = para.listRendering;
|
|
41
|
+
if (numPr?.kind === "none" || pkg?.numbering !== void 0 && numPr === void 0) list = void 0;
|
|
42
|
+
else if (pkg?.numbering !== void 0 && numPr?.kind === "reference") list = resolveCachedListRendering(para.listRendering, resolveListRenderingDefinition(numPr, getCachedNumberingMap(pkg.numbering))) ?? void 0;
|
|
43
|
+
const label = ctx.nextListLabel(list ? listLabelAttrsFromRendering(list, numPrFromStyle) : UNNUMBERED);
|
|
36
44
|
const inline = renderParagraphInline(ctx, pkg, para.content, para.paraId);
|
|
37
45
|
const styleId = para.formatting?.styleId;
|
|
38
46
|
const headingLevel = markdownHeadingLevel(ctx, para);
|
|
@@ -49,8 +57,8 @@ function renderParagraphBlock(ctx, pkg, para) {
|
|
|
49
57
|
isListItem: false
|
|
50
58
|
};
|
|
51
59
|
}
|
|
52
|
-
if (
|
|
53
|
-
markdown: renderListItem(ctx,
|
|
60
|
+
if (list && label !== void 0) return {
|
|
61
|
+
markdown: renderListItem(ctx, list, label, inline),
|
|
54
62
|
isListItem: true
|
|
55
63
|
};
|
|
56
64
|
ctx.listIndentWidths = [];
|
|
@@ -72,15 +80,6 @@ function markdownHeadingLevel(ctx, para) {
|
|
|
72
80
|
}
|
|
73
81
|
const UNNUMBERED = Object.freeze({});
|
|
74
82
|
/**
|
|
75
|
-
* The attrs the page's list counter reads, projected from the paragraph the
|
|
76
|
-
* way the editor projects them (`toProseDoc`). A paragraph without a list
|
|
77
|
-
* rendering counts as unnumbered.
|
|
78
|
-
*/
|
|
79
|
-
function listLabelAttrs(para) {
|
|
80
|
-
const list = para.listRendering;
|
|
81
|
-
return list ? listLabelAttrsFromRendering(list, para.formatting?.numPrFromStyle) : UNNUMBERED;
|
|
82
|
-
}
|
|
83
|
-
/**
|
|
84
83
|
* A plain paragraph whose visible text begins with markdown block syntax (e.g.
|
|
85
84
|
* `# Not a heading`, `- value`, `1. value`, `> quote`) would be reclassified as
|
|
86
85
|
* a heading/list/blockquote on re-parse, even though Word carries no matching
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { getHyperlinkRuns } from "../docx/hyperlinkParser.js";
|
|
2
|
-
import { RELATIONSHIP_TYPES, resolveRelationshipIdOfType } from "../docx/relsParser.js";
|
|
2
|
+
import { RELATIONSHIP_TYPES, resolveRelationshipIdOfType, resolveRelativePath } from "../docx/relsParser.js";
|
|
3
3
|
import { decodeOoxmlSymbolCharacter } from "../utils/ooxmlSymbol.js";
|
|
4
4
|
import { wrapComment, wrapDeletion, wrapInsertion, wrapMoveFrom, wrapMoveTo } from "./annotations.js";
|
|
5
5
|
import { escapeAltText, escapeInline, escapeLinkUrl } from "./escape.js";
|
|
@@ -226,7 +226,7 @@ function renderRunContent(ctx, pkg, content, paraId) {
|
|
|
226
226
|
case "noteMarker": break;
|
|
227
227
|
case "drawing": {
|
|
228
228
|
const ref = resolveRelationshipIdOfType(pkg?.relationships, item.image.rId, RELATIONSHIP_TYPES.image);
|
|
229
|
-
const media = ref.status === "resolved" ? pkg?.media?.get(ref.relationship.target) : void 0;
|
|
229
|
+
const media = ref.status === "resolved" ? pkg?.media?.get(resolveRelativePath("word/document.xml", ref.relationship.target)) : void 0;
|
|
230
230
|
if (media) {
|
|
231
231
|
const reg = registerImage(ctx, media, item.image, paraId);
|
|
232
232
|
const alt = reg.alt ? escapeAltText(reg.alt) : "";
|
package/dist/markdown/types.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { document_d_exports } from "../types/document.js";
|
|
1
2
|
import { NoteReferenceKind, NoteReferenceNumbering } from "../utils/noteReferenceLabels.js";
|
|
2
3
|
import { BuiltInStyleIndex } from "../docx/builtInStyles.js";
|
|
3
4
|
import { ListLabelCounter } from "../prosemirror/listLabels.js";
|
|
@@ -140,6 +141,8 @@ type RenderContext = {
|
|
|
140
141
|
nextListLabel: ListLabelCounter;
|
|
141
142
|
/** The document's styles indexed by the built-in they are; classifies headings and quotes. */
|
|
142
143
|
builtInStyles: BuiltInStyleIndex;
|
|
144
|
+
/** Authoritative paragraph numbering inherited through the style cascade. */
|
|
145
|
+
paragraphNumbering: (styleId: string | undefined) => document_d_exports.ParagraphFormatting["numPr"];
|
|
143
146
|
/**
|
|
144
147
|
* Active native Markdown ancestor marker widths, indexed by OOXML level.
|
|
145
148
|
* Missing levels contribute no columns. Prose, headings and container
|
|
@@ -8,6 +8,7 @@ import { attrsRecord, attrsResult, expectAttrs, expectCachedNodeAttrs, expectNod
|
|
|
8
8
|
import { COUNTER_FORMAT_VALUES, EMPHASIS_MARK_VALUES, FIELD_TYPE_VALUES, FONT_HINT_VALUES, FONT_THEME_VALUES, HIGHLIGHT_COLOR_VALUES, IMAGE_HORIZONTAL_ALIGNMENT_VALUES, IMAGE_HORIZONTAL_RELATIVE_TO_VALUES, IMAGE_VERTICAL_ALIGNMENT_VALUES, IMAGE_VERTICAL_RELATIVE_TO_VALUES, IMAGE_WRAP_TEXT_VALUES, IMAGE_WRAP_TYPE_VALUES, LINE_SPACING_RULE_VALUES, OUTLINE_STYLE_ATTR_VALUES, PARAGRAPH_ALIGNMENT_VALUES, POSITIONAL_TAB_ALIGNMENT_VALUES, POSITIONAL_TAB_LEADER_VALUES, POSITIONAL_TAB_RELATIVE_TO_VALUES, SDT_LOCK_VALUES, SDT_TYPE_VALUES, SHADING_PATTERN_VALUES, TABLE_CELL_VERTICAL_ALIGNMENT_VALUES, TABLE_JUSTIFICATION_VALUES, TABLE_ROW_HEIGHT_RULE_VALUES, TABLE_WIDTH_TYPE_VALUES, TAB_LEADER_VALUES, TAB_STOP_ALIGNMENT_VALUES, TEXT_DIRECTION_VALUES, TEXT_EFFECT_VALUES, UNDERLINE_STYLE_VALUES } from "../../types/documentEnumValues.js";
|
|
9
9
|
import { canonicalJson } from "../../utils/canonicalJson.js";
|
|
10
10
|
import { normalizeHorizontalScalePercent } from "../../utils/horizontalScale.js";
|
|
11
|
+
import { PICTURE_NAME_DOM_ATTRIBUTES } from "../imagePictureNames.js";
|
|
11
12
|
import { isNoteReferenceOccurrenceId } from "../noteReferenceOccurrenceId.js";
|
|
12
13
|
import { paragraphNumberingFromAttrValue } from "../numberingAttr.js";
|
|
13
14
|
import { outlineLevelFromAttrValue } from "../outlineLevelAttr.js";
|
|
@@ -489,6 +490,9 @@ const readImageAttrs = (node) => {
|
|
|
489
490
|
expectNodeType(node, "image", issues);
|
|
490
491
|
requiredString(attrs, "src", "image.attrs.src", issues);
|
|
491
492
|
optionalString(attrs, "docPrName", "image.attrs.docPrName", issues);
|
|
493
|
+
optionalNestedRecord(attrs, "pictureNames", "image.attrs.pictureNames", issues, (names, path, nestedIssues) => {
|
|
494
|
+
for (const key of Object.keys(PICTURE_NAME_DOM_ATTRIBUTES)) optionalString(names, key, `${path}.${key}`, nestedIssues);
|
|
495
|
+
});
|
|
492
496
|
optionalString(attrs, "alt", "image.attrs.alt", issues);
|
|
493
497
|
optionalString(attrs, "title", "image.attrs.title", issues);
|
|
494
498
|
optionalString(attrs, "rId", "image.attrs.rId", issues);
|
|
@@ -16,11 +16,18 @@ import { Node } from "prosemirror-model";
|
|
|
16
16
|
type ProjectionReuse = "none" | "matched";
|
|
17
17
|
/** How the conversion treats records the editor did not change. */
|
|
18
18
|
type FromProseDocOptions = {
|
|
19
|
+
/** The stylesheet authority used to construct the projection. */
|
|
20
|
+
stylesheetSource: {
|
|
21
|
+
type: "package";
|
|
22
|
+
} | {
|
|
23
|
+
type: "supplied";
|
|
24
|
+
styles: document_d_exports.StyleDefinitions;
|
|
25
|
+
};
|
|
19
26
|
/** Defaults to `"none"`, the only value implemented. */
|
|
20
27
|
reuse?: ProjectionReuse;
|
|
21
28
|
};
|
|
22
29
|
/** Convert a ProseMirror document to the document model. */
|
|
23
|
-
declare function fromProseDoc(pmDoc: Node, baseDocument
|
|
30
|
+
declare function fromProseDoc(pmDoc: Node, baseDocument: document_d_exports.Document | undefined, { stylesheetSource, reuse }: FromProseDocOptions): document_d_exports.Document;
|
|
24
31
|
declare function tableAttrsToFormatting(attrs: TableAttrs): document_d_exports.TableFormatting | undefined;
|
|
25
32
|
/**
|
|
26
33
|
* Convert ProseMirror table row attrs to TableRowFormatting
|