@stll/folio-core 0.66.1 → 0.66.2

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.
Files changed (80) hide show
  1. package/dist/ai-edits/apply.d.ts +13 -1
  2. package/dist/ai-edits/apply.js +533 -136
  3. package/dist/ai-edits/batch-claims.d.ts +26 -0
  4. package/dist/ai-edits/batch-claims.js +30 -5
  5. package/dist/ai-edits/headless.d.ts +6 -0
  6. package/dist/ai-edits/headless.js +25 -3
  7. package/dist/ai-edits/newListNumbering.d.ts +4 -1
  8. package/dist/ai-edits/newListNumbering.js +4 -2
  9. package/dist/ai-edits/types.d.ts +1 -1
  10. package/dist/document-operations.d.ts +13 -1
  11. package/dist/document-operations.js +11 -4
  12. package/dist/docx/listNumberingInstances.d.ts +6 -4
  13. package/dist/docx/listNumberingInstances.js +12 -11
  14. package/dist/docx/numberingIds.d.ts +10 -0
  15. package/dist/docx/numberingIds.js +5 -1
  16. package/dist/docx/paragraphParser.js +16 -3
  17. package/dist/docx/paragraphProperties.js +2 -1
  18. package/dist/docx/serializer/paragraphSerializer.js +86 -66
  19. package/dist/i18n/messages/ar.gen.js +1 -0
  20. package/dist/i18n/messages/catalogs.gen.d.ts +17 -0
  21. package/dist/i18n/messages/catalogs.gen.js +17 -0
  22. package/dist/i18n/messages/cs.gen.js +1 -0
  23. package/dist/i18n/messages/de.gen.js +1 -0
  24. package/dist/i18n/messages/en.gen.js +1 -0
  25. package/dist/i18n/messages/es.gen.js +1 -0
  26. package/dist/i18n/messages/et.gen.js +1 -0
  27. package/dist/i18n/messages/fr.gen.js +1 -0
  28. package/dist/i18n/messages/he.gen.js +1 -0
  29. package/dist/i18n/messages/hi.gen.js +1 -0
  30. package/dist/i18n/messages/hu.gen.js +1 -0
  31. package/dist/i18n/messages/lt.gen.js +1 -0
  32. package/dist/i18n/messages/lv.gen.js +1 -0
  33. package/dist/i18n/messages/messages.gen.d.ts +1 -0
  34. package/dist/i18n/messages/pl.gen.js +1 -0
  35. package/dist/i18n/messages/pt-BR.gen.js +1 -0
  36. package/dist/i18n/messages/sk.gen.js +1 -0
  37. package/dist/i18n/messages/tr.gen.js +1 -0
  38. package/dist/i18n/messages/zh-CN.gen.js +1 -0
  39. package/dist/internal/wholeStoryRevisionResolution.d.ts +1 -1
  40. package/dist/internal/wholeStoryRevisionResolution.js +24 -9
  41. package/dist/layout-bridge/convert/runMarkFormatting.js +1 -0
  42. package/dist/markdown/renderRuns.js +68 -12
  43. package/dist/prosemirror/commands/comments.js +2 -0
  44. package/dist/prosemirror/commands/resolveParagraphProperties.js +15 -2
  45. package/dist/prosemirror/conversion/fromProseDoc.js +55 -18
  46. package/dist/prosemirror/conversion/hyphenTextCarriers.d.ts +23 -1
  47. package/dist/prosemirror/conversion/hyphenTextCarriers.js +46 -1
  48. package/dist/prosemirror/conversion/toProseDoc.js +15 -9
  49. package/dist/prosemirror/extensions/StarterKit.js +1 -1
  50. package/dist/prosemirror/extensions/features/pasteCleanup.js +6 -0
  51. package/dist/prosemirror/extensions/features/pastedHtmlLists.js +2 -1
  52. package/dist/prosemirror/extensions/markRegistry.d.ts +3 -1
  53. package/dist/prosemirror/extensions/markRegistry.js +3 -0
  54. package/dist/prosemirror/extensions/marks/HyphenElementExtension.d.ts +6 -0
  55. package/dist/prosemirror/extensions/marks/HyphenElementExtension.js +54 -0
  56. package/dist/prosemirror/fieldRepresentation.js +2 -1
  57. package/dist/prosemirror/hyphenElementAttrs.d.ts +10 -0
  58. package/dist/prosemirror/hyphenElementAttrs.js +32 -0
  59. package/dist/prosemirror/keptRangeBoundaries.d.ts +2 -0
  60. package/dist/prosemirror/keptRangeBoundaries.js +1 -1
  61. package/dist/prosemirror/listNumbering.js +3 -2
  62. package/dist/prosemirror/paragraphIndentation.js +2 -1
  63. package/dist/prosemirror/paragraphPropertyCarry.d.ts +1 -14
  64. package/dist/prosemirror/paragraphPropertyCarry.js +4 -28
  65. package/dist/prosemirror/plugins/suggestionMode.js +19 -6
  66. package/dist/prosemirror/replacedAnnotations.d.ts +1 -0
  67. package/dist/prosemirror/replacedAnnotations.js +1 -0
  68. package/dist/prosemirror/sectionCarrier.d.ts +8 -0
  69. package/dist/prosemirror/sectionCarrier.js +29 -1
  70. package/dist/prosemirror/styles/paragraphStyleCascade.d.ts +10 -0
  71. package/dist/prosemirror/styles/paragraphStyleCascade.js +12 -2
  72. package/dist/prosemirror/tableRunIn.d.ts +24 -9
  73. package/dist/prosemirror/tableRunIn.js +107 -21
  74. package/dist/prosemirror/textInput.js +3 -1
  75. package/dist/prosemirror/validation.js +6 -0
  76. package/dist/utils/createDocument.js +11 -8
  77. package/dist/utils/formatToStyle.js +2 -2
  78. package/dist/utils/units.d.ts +5 -0
  79. package/dist/utils/units.js +6 -1
  80. package/package.json +3 -3
@@ -124,6 +124,7 @@ const EMPTY_SPAN = new RegExp(`<span(?:\\s${TAG_TAIL})?><\\/span>`, "gi");
124
124
  const MAX_EMPTY_SPAN_PASSES = 5;
125
125
  const INTERNAL_CLIPBOARD_ATTR_PATTERN = /\s+data-docx-internal-clipboard(?:="([^"]*)"|='([^']*)'|=([^\s>]+))?/gi;
126
126
  const TEXTBOX_ANCHOR_ATTR = /\s+data-docx-textbox-anchor(?:="[^"]*"|='[^']*')?/gi;
127
+ const HYPHEN_ELEMENT_ATTR = /\s+data-docx-hyphen-element(?:="[^"]*"|='[^']*'|=[^\s>]+)?/gi;
127
128
  const BOOKMARK_BOUNDARY_ATTR = /\s+data-docx-bookmark-(?:boundary|id|name|col-first|col-last)(?:="[^"]*"|='[^']*'|=[^\s>]+)?/gi;
128
129
  const RANGE_ANCHOR_ATTR = /\s+data-docx-range-anchor(?:="[^"]*"|='[^']*'|=[^\s>]+)?/gi;
129
130
  const COMMENT_RANGE_BOUNDARY_ATTR = /\s+data-docx-comment-range-boundary(?:="[^"]*"|='[^']*'|=[^\s>]+)?/gi;
@@ -145,6 +146,10 @@ function stripForeignTextBoxAnchors(html, internal) {
145
146
  if (internal) return html;
146
147
  return html.replace(TEXTBOX_ANCHOR_ATTR, "");
147
148
  }
149
+ function stripForeignHyphenElements(html, internal) {
150
+ if (internal) return html;
151
+ return html.replace(HYPHEN_ELEMENT_ATTR, "");
152
+ }
148
153
  function stripForeignBookmarkBoundaries(html, internal) {
149
154
  if (internal) return html;
150
155
  return html.replace(BOOKMARK_BOUNDARY_ATTR, "");
@@ -187,6 +192,7 @@ function cleanPastedHtml(html, options = {}) {
187
192
  cleaned = stripMsoStyles(cleaned);
188
193
  cleaned = stripEmptySpans(cleaned, internal);
189
194
  cleaned = stripForeignTextBoxAnchors(cleaned, internal);
195
+ cleaned = stripForeignHyphenElements(cleaned, internal);
190
196
  cleaned = stripForeignBookmarkBoundaries(cleaned, internal);
191
197
  cleaned = stripForeignRangeCarriers(cleaned, internal);
192
198
  cleaned = stripForeignStructuredFields(cleaned, internal);
@@ -1,4 +1,5 @@
1
1
  import { mintListInstance } from "../../../docx/listNumberingInstances.js";
2
+ import { NO_NUMBERING_IDS_IN_USE } from "../../../docx/numberingIds.js";
2
3
  import { createNumberingMap } from "../../../docx/numberingParser.js";
3
4
  import { expectParagraphAttrs } from "../../attrs/index.js";
4
5
  import { completeNumberingForDoc } from "../../listInstanceReferences.js";
@@ -93,7 +94,7 @@ const numberPastedHtmlLists = (slice, view) => {
93
94
  const minted = mintListInstance(definitions, {
94
95
  kind: hint.kind,
95
96
  ...hint.start === void 0 ? {} : { start: hint.start }
96
- });
97
+ }, NO_NUMBERING_IDS_IN_USE);
97
98
  definitions = minted.definitions;
98
99
  numbering = createNumberingMap(definitions);
99
100
  group = {
@@ -1,4 +1,5 @@
1
1
  import { MarkExtension } from "./types.js";
2
+ import { HyphenElementExtension } from "./marks/HyphenElementExtension.js";
2
3
  export declare const MARK_EXTENSIONS: {
3
4
  readonly bold: (options?: Partial<Record<string, unknown>> | undefined) => MarkExtension;
4
5
  readonly italic: (options?: Partial<Record<string, unknown>> | undefined) => MarkExtension;
@@ -27,6 +28,7 @@ export declare const MARK_EXTENSIONS: {
27
28
  readonly textEffect: (options?: Partial<Record<string, unknown>> | undefined) => MarkExtension;
28
29
  readonly runFormattingOverride: (options?: Partial<Record<string, unknown>> | undefined) => MarkExtension;
29
30
  readonly characterStyle: (options?: Partial<Record<string, unknown>> | undefined) => MarkExtension;
31
+ readonly hyphenElement: typeof HyphenElementExtension;
30
32
  readonly runIdentity: (options?: Partial<Record<string, unknown>> | undefined) => MarkExtension;
31
33
  readonly comment: (options?: Partial<Record<string, unknown>> | undefined) => MarkExtension;
32
34
  readonly insertion: (options?: Partial<Record<string, unknown>> | undefined) => MarkExtension;
@@ -56,4 +58,4 @@ export type SchemaMarkName = keyof typeof MARK_EXTENSIONS;
56
58
  * own in OOXML, so the save leg ranks it against nothing and its place here is
57
59
  * a presentation decision.
58
60
  */
59
- export declare const MARK_NESTING_ORDER: readonly ["bold", "italic", "underline", "strike", "textColor", "runShading", "highlight", "fontSize", "fontFamily", "language", "superscript", "subscript", "allCaps", "smallCaps", "footnoteRef", "characterSpacing", "emboss", "imprint", "hidden", "textShadow", "emphasisMark", "textOutline", "rtl", "textEffect", "runFormattingOverride", "characterStyle", "comment", "inlineWrapper", "insertion", "deletion", "hyperlink", "runPropertyChange", "runIdentity"];
61
+ export declare const MARK_NESTING_ORDER: readonly ["bold", "italic", "underline", "strike", "textColor", "runShading", "highlight", "fontSize", "fontFamily", "language", "superscript", "subscript", "allCaps", "smallCaps", "footnoteRef", "characterSpacing", "emboss", "imprint", "hidden", "textShadow", "emphasisMark", "textOutline", "rtl", "textEffect", "runFormattingOverride", "characterStyle", "hyphenElement", "comment", "inlineWrapper", "insertion", "deletion", "hyperlink", "runPropertyChange", "runIdentity"];
@@ -9,6 +9,7 @@ import { FootnoteRefExtension } from "./marks/FootnoteRefExtension.js";
9
9
  import { HiddenTextExtension } from "./marks/HiddenTextExtension.js";
10
10
  import { HighlightExtension } from "./marks/HighlightExtension.js";
11
11
  import { HyperlinkExtension } from "./marks/HyperlinkExtension.js";
12
+ import { HyphenElementExtension } from "./marks/HyphenElementExtension.js";
12
13
  import { InlineWrapperExtension } from "./marks/InlineWrapperExtension.js";
13
14
  import { ItalicExtension } from "./marks/ItalicExtension.js";
14
15
  import { LanguageExtension } from "./marks/LanguageExtension.js";
@@ -67,6 +68,7 @@ const MARK_EXTENSIONS = {
67
68
  textEffect: TextEffectExtension,
68
69
  runFormattingOverride: RunFormattingOverrideExtension,
69
70
  characterStyle: CharacterStyleExtension,
71
+ hyphenElement: HyphenElementExtension,
70
72
  runIdentity: RunIdentityExtension,
71
73
  comment: CommentExtension,
72
74
  insertion: InsertionExtension,
@@ -123,6 +125,7 @@ const MARK_NESTING_ORDER = markNestingOrder([
123
125
  "textEffect",
124
126
  "runFormattingOverride",
125
127
  "characterStyle",
128
+ "hyphenElement",
126
129
  "comment",
127
130
  "inlineWrapper",
128
131
  "insertion",
@@ -0,0 +1,6 @@
1
+ import { MarkExtension } from "../types.js";
2
+ type HyphenElementOptions = {
3
+ getInternalClipboardToken?: () => string;
4
+ };
5
+ export declare const HyphenElementExtension: (options?: Partial<HyphenElementOptions> | undefined) => MarkExtension;
6
+ export {};
@@ -0,0 +1,54 @@
1
+ import { HYPHEN_ELEMENT_MARK_NAME } from "../../conversion/hyphenTextCarriers.js";
2
+ import { expectHyphenElementMarkAttrs } from "../../hyphenElementAttrs.js";
3
+ import { createMarkExtension } from "../create.js";
4
+ // src/prosemirror/extensions/marks/HyphenElementExtension.ts
5
+ /**
6
+ * Marks the native text that stands for a `w:softHyphen` or `w:noBreakHyphen`
7
+ * run child (ECMA-376 Part 1, 17.3.3.29 and 17.3.3.18).
8
+ *
9
+ * The editor renders both elements as their Unicode characters so line
10
+ * breaking and measurement treat them as text. The same characters may also be
11
+ * authored inside `w:t`, so without this mark the save leg could not tell
12
+ * which of the two the source held. Nothing is painted for it.
13
+ *
14
+ * Not inclusive, and each carrier in a paragraph has its own `id`: ProseMirror
15
+ * keeps a non-inclusive mark only where both neighbours hold an equal mark, so
16
+ * distinct ids mean a character typed beside or between carriers is text. The
17
+ * ids only tell neighbours apart. They can repeat after a paste beside the
18
+ * source or a paragraph join, which `hyphenElementRunContent` describes.
19
+ *
20
+ * External HTML must not be able to claim the element: `cleanPastedHtml` strips
21
+ * the attribute unless the slice carries the internal clipboard capability, so
22
+ * `toDOM` stamps that capability on every carrier it writes.
23
+ */
24
+ const HYPHEN_ELEMENT_ATTRIBUTE = "data-docx-hyphen-element";
25
+ const CANONICAL_HYPHEN_ELEMENT_ID = /^(?:0|[1-9]\d*)$/u;
26
+ const HyphenElementExtension = createMarkExtension({
27
+ name: HYPHEN_ELEMENT_MARK_NAME,
28
+ schemaMarkName: HYPHEN_ELEMENT_MARK_NAME,
29
+ markSpec: (options) => ({
30
+ attrs: { id: {} },
31
+ inclusive: false,
32
+ parseDOM: [{
33
+ tag: `span[${HYPHEN_ELEMENT_ATTRIBUTE}]`,
34
+ getAttrs(dom) {
35
+ const rawId = dom.getAttribute(HYPHEN_ELEMENT_ATTRIBUTE);
36
+ if (rawId === null || !CANONICAL_HYPHEN_ELEMENT_ID.test(rawId)) return false;
37
+ const id = Number(rawId);
38
+ return Number.isSafeInteger(id) ? { id } : false;
39
+ }
40
+ }],
41
+ toDOM(mark) {
42
+ const { id } = expectHyphenElementMarkAttrs(mark);
43
+ return [
44
+ "span",
45
+ {
46
+ [HYPHEN_ELEMENT_ATTRIBUTE]: String(id),
47
+ ...options.getInternalClipboardToken ? { "data-docx-internal-clipboard": options.getInternalClipboardToken() } : {}
48
+ },
49
+ 0
50
+ ];
51
+ }
52
+ })
53
+ });
54
+ export { HyphenElementExtension };
@@ -1,3 +1,4 @@
1
+ import "./conversion/hyphenTextCarriers.js";
1
2
  import "./extensions/marks/InlineWrapperExtension.js";
2
3
  import { getInlineDisplayText } from "./inlineDisplayText.js";
3
4
  import "./runIdentity.js";
@@ -11,7 +12,7 @@ const fieldCommentCoverage = (fieldKind) => FIELD_COMMENT_COVERAGE[fieldKind];
11
12
  /** Current children alone decide whether a field needs editable structural content. */
12
13
  const fieldRequiresStructuredContent = (children) => {
13
14
  const first = children.at(0);
14
- return children.some((child) => !child.isText || first !== void 0 && !Mark.sameSet(first.marks, child.marks) || child.marks.some((mark) => mark.type.name === "runIdentity" || mark.type.name === "footnoteRef" || mark.type.name === "hyperlink" || mark.type.name === "inlineWrapper" || mark.type.name === "insertion" || mark.type.name === "deletion"));
15
+ return children.some((child) => !child.isText || first !== void 0 && !Mark.sameSet(first.marks, child.marks) || child.marks.some((mark) => mark.type.name === "runIdentity" || mark.type.name === "hyphenElement" || mark.type.name === "footnoteRef" || mark.type.name === "hyperlink" || mark.type.name === "inlineWrapper" || mark.type.name === "insertion" || mark.type.name === "deletion"));
15
16
  };
16
17
  /** Resolution collapses a field only when the atom can retain its result. */
17
18
  const canonicalFieldNode = (node) => {
@@ -0,0 +1,10 @@
1
+ import { ReadProseMirrorAttrsResult } from "../internal/prosemirrorAttrBoundary.js";
2
+ import "./attrs/index.js";
3
+ import { Mark } from "prosemirror-model";
4
+ type HyphenElementMarkAttrs = {
5
+ /** Tells this carrier from its neighbours. Monotonic within one paragraph. */
6
+ id: number;
7
+ };
8
+ export declare const readHyphenElementMarkAttrs: (mark: Mark) => ReadProseMirrorAttrsResult<HyphenElementMarkAttrs>;
9
+ export declare const expectHyphenElementMarkAttrs: (mark: Mark) => HyphenElementMarkAttrs;
10
+ export {};
@@ -0,0 +1,32 @@
1
+ import { panic } from "better-result";
2
+ // src/prosemirror/hyphenElementAttrs.ts
3
+ const readHyphenElementMarkAttrs = (mark) => {
4
+ const id = mark.attrs["id"];
5
+ if (mark.type.name !== "hyphenElement") return {
6
+ ok: false,
7
+ issues: [{
8
+ path: "mark.type.name",
9
+ message: `Expected hyphenElement, got ${mark.type.name}.`
10
+ }]
11
+ };
12
+ if (typeof id !== "number" || !Number.isSafeInteger(id) || id < 0) return {
13
+ ok: false,
14
+ issues: [{
15
+ path: "hyphenElement.attrs.id",
16
+ message: "Expected a non-negative safe integer."
17
+ }]
18
+ };
19
+ return {
20
+ ok: true,
21
+ value: { id }
22
+ };
23
+ };
24
+ const expectHyphenElementMarkAttrs = (mark) => {
25
+ const result = readHyphenElementMarkAttrs(mark);
26
+ if (!result.ok) {
27
+ const details = result.issues.map((issue) => `${issue.path}: ${issue.message}`).join("\n");
28
+ panic(`Invalid ProseMirror hyphen element attrs:\n${details}`);
29
+ }
30
+ return result.value;
31
+ };
32
+ export { expectHyphenElementMarkAttrs, readHyphenElementMarkAttrs };
@@ -6,6 +6,8 @@ import { Transaction } from "prosemirror-state";
6
6
  type BoundaryItem = Extract<document_d_exports.ParagraphContent, {
7
7
  type: "bookmarkStart" | "bookmarkEnd" | "commentRangeStart" | "commentRangeEnd" | "moveFromRangeStart" | "moveFromRangeEnd" | "moveToRangeStart" | "moveToRangeEnd";
8
8
  }>;
9
+ /** The marks that say a node is in a revision: its wrappers, in the engine. */
10
+ export declare const REVISION_MARK_NAMES: ReadonlySet<string>;
9
11
  /** The range boundaries `node` carries; none for any other node. */
10
12
  export declare const rangeBoundaryItemsOf: (node: Node) => readonly BoundaryItem[];
11
13
  /** Range boundary nodes, as the nodes that stand for them between blocks. */
@@ -377,4 +377,4 @@ const removeResolvedSubtree = (node, selection) => {
377
377
  if (selection.mode === "reject") visit(node);
378
378
  return { kept };
379
379
  };
380
- export { blockBoundariesOf, carriesCommentAnchor, inlineBoundariesOf, isBlockBoundaryNode, normalizeKeptRangeBoundaries, paragraphRemainsOf, rangeBoundaryItemsOf, removeResolvedSubtree, revisionLayerRemovesContent };
380
+ export { REVISION_MARK_NAMES, blockBoundariesOf, carriesCommentAnchor, inlineBoundariesOf, isBlockBoundaryNode, normalizeKeptRangeBoundaries, paragraphRemainsOf, rangeBoundaryItemsOf, removeResolvedSubtree, revisionLayerRemovesContent };
@@ -1,4 +1,5 @@
1
1
  import { mintListInstance, restartListInstance } from "../docx/listNumberingInstances.js";
2
+ import { NO_NUMBERING_IDS_IN_USE } from "../docx/numberingIds.js";
2
3
  import { createNumberingMap, isBulletLevel } from "../docx/numberingParser.js";
3
4
  import { paragraphNumberingReferenceId } from "../docx/numberingReference.js";
4
5
  import { expectParagraphAttrs } from "./attrs/index.js";
@@ -179,7 +180,7 @@ const resolveListTarget = ({ numbering, $from, $to, requests }) => {
179
180
  ilvl: 0,
180
181
  numbering
181
182
  };
182
- const minted = mintListInstance(numbering?.definitions, first);
183
+ const minted = mintListInstance(numbering?.definitions, first, NO_NUMBERING_IDS_IN_USE);
183
184
  return {
184
185
  numId: minted.numId,
185
186
  ilvl: 0,
@@ -289,7 +290,7 @@ const setNumberingValue = (value) => withCanonicalCommand((state, dispatch) => {
289
290
  abstractNumId,
290
291
  ilvl: selected.membership.ilvl,
291
292
  start: value
292
- });
293
+ }, NO_NUMBERING_IDS_IN_USE);
293
294
  dispatch(moveItems({
294
295
  state,
295
296
  items: itemsFrom(state, selected.membership.numId, selected.pos),
@@ -1,3 +1,4 @@
1
+ import { hangingFirstLineIndent } from "../utils/units.js";
1
2
  import { effectiveParagraphNumberingReference, paragraphNumberingSourcesFromBoundary } from "./numberingAttr.js";
2
3
  import { panic } from "better-result";
3
4
  // src/prosemirror/paragraphIndentation.ts
@@ -70,7 +71,7 @@ const directParagraphIndentation = (attrs) => {
70
71
  if (indentation.indentFirstLine !== void 0) {
71
72
  const hanging = attrs.hangingIndent === true;
72
73
  indentation.hangingIndent = hanging;
73
- if (hanging) indentation.indentFirstLine = -Math.abs(indentation.indentFirstLine);
74
+ if (hanging) indentation.indentFirstLine = hangingFirstLineIndent(indentation.indentFirstLine);
74
75
  } else if (original?.hangingIndent !== void 0 && original.indentFirstLine === void 0 && typeof attrs.hangingIndent === "boolean") indentation.hangingIndent = attrs.hangingIndent;
75
76
  return Object.keys(indentation).length > 0 ? indentation : void 0;
76
77
  };
@@ -3,7 +3,6 @@ import { NumberingMap } from "../docx/numberingParser.js";
3
3
  import { RunStyleResolver } from "./runStyleFormatting.js";
4
4
  import { Node } from "prosemirror-model";
5
5
  import { Transaction } from "prosemirror-state";
6
- type RecordedFormatting = NonNullable<ParagraphPropertyChangeAttrs["previousFormatting"]>;
7
6
  type CarryParagraphPropertiesOptions = {
8
7
  tr: Transaction;
9
8
  /** The paragraph that takes the properties. */
@@ -14,14 +13,7 @@ type CarryParagraphPropertiesOptions = {
14
13
  numbering: NumberingMap | null;
15
14
  /** Records the change as a tracked property change; applied directly when omitted. */
16
15
  revision?: ParagraphPropertyChangeAttrs["info"];
17
- /** Properties the paragraph keeps its own values of: ones its own batch set. */
18
- keep?: ReadonlySet<string>;
19
16
  };
20
- type CarriedParagraphPropertiesOptions = Pick<CarryParagraphPropertiesOptions, "source" | "keep"> & {
21
- target: Node;
22
- };
23
- /** The exact formatting transfer, shared with pre-mutation capacity checks. */
24
- export declare const carriedParagraphProperties: ({ source, target, keep }: CarriedParagraphPropertiesOptions) => RecordedFormatting;
25
17
  /**
26
18
  * Give a paragraph another one's properties: the pPr a property change
27
19
  * covers. Its identity, mark, mark run properties and section stay its own,
@@ -29,7 +21,7 @@ export declare const carriedParagraphProperties: ({ source, target, keep }: Carr
29
21
  * change now records what it had: a paragraph holds one, and a pending one
30
22
  * already records it.
31
23
  */
32
- export declare const carryParagraphProperties: ({ tr, position, source, styleResolver, numbering, revision, keep }: CarryParagraphPropertiesOptions) => {
24
+ export declare const carryParagraphProperties: ({ tr, position, source, styleResolver, numbering, revision }: CarryParagraphPropertiesOptions) => {
33
25
  tr: Transaction;
34
26
  changed: boolean;
35
27
  tracked: boolean;
@@ -60,9 +52,4 @@ export declare const recordReplacedParagraphProperties: ({ tr, position, replace
60
52
  replaced: Node;
61
53
  revision: ParagraphPropertyChangeAttrs["info"];
62
54
  }) => boolean;
63
- /**
64
- * The properties a batch's own property change set on a paragraph: the ones
65
- * whose value differs from what that change records as before.
66
- */
67
- export declare const propertiesSetInBatch: (node: Node, batchRevisionIds: ReadonlySet<number>) => ReadonlySet<string>;
68
55
  export {};
@@ -18,8 +18,6 @@ import { panic } from "better-result";
18
18
  * revision, so accepting reads as the direct edit and rejecting restores the
19
19
  * second's own.
20
20
  */
21
- /** The exact formatting transfer, shared with pre-mutation capacity checks. */
22
- const carriedParagraphProperties = ({ source, target, keep }) => Object.fromEntries([...Object.entries(paragraphPropertiesSnapshot(source)).filter(([key]) => !keep?.has(key)), ...Object.entries(paragraphPropertiesSnapshot(target)).filter(([key]) => keep?.has(key))]);
23
21
  /**
24
22
  * Give a paragraph another one's properties: the pPr a property change
25
23
  * covers. Its identity, mark, mark run properties and section stay its own,
@@ -27,7 +25,7 @@ const carriedParagraphProperties = ({ source, target, keep }) => Object.fromEntr
27
25
  * change now records what it had: a paragraph holds one, and a pending one
28
26
  * already records it.
29
27
  */
30
- const carryParagraphProperties = ({ tr, position, source, styleResolver, numbering, revision, keep }) => {
28
+ const carryParagraphProperties = ({ tr, position, source, styleResolver, numbering, revision }) => {
31
29
  const target = tr.doc.nodeAt(position);
32
30
  if (!target || target.type !== source.type) return {
33
31
  tr,
@@ -35,11 +33,7 @@ const carryParagraphProperties = ({ tr, position, source, styleResolver, numberi
35
33
  tracked: false
36
34
  };
37
35
  const previousFormatting = paragraphPropertiesSnapshot(target);
38
- const formatting = carriedParagraphProperties({
39
- source,
40
- target,
41
- ...keep === void 0 ? {} : { keep }
42
- });
36
+ const formatting = paragraphPropertiesSnapshot(source);
43
37
  if (JSON.stringify(previousFormatting) === JSON.stringify(formatting)) return {
44
38
  tr,
45
39
  changed: false,
@@ -74,7 +68,7 @@ const carryParagraphProperties = ({ tr, position, source, styleResolver, numberi
74
68
  }] : []];
75
69
  tr.setNodeMarkup(position, void 0, {
76
70
  ...carried,
77
- ...carriedNumberingProvenance(source, target, keep),
71
+ numPrFromStyle: expectParagraphAttrs(source).numPrFromStyle,
78
72
  _sectionProperties: target.attrs["_sectionProperties"],
79
73
  _propertyChanges: changes.length > 0 ? changes : null
80
74
  });
@@ -91,12 +85,6 @@ const carryParagraphProperties = ({ tr, position, source, styleResolver, numberi
91
85
  };
92
86
  };
93
87
  /**
94
- * Whether the carried numbering is the paragraph's own or its style's: the
95
- * record holds only stated numbering, and a style's numbering
96
- * stays the style's rather than becoming the paragraph's own `w:numPr`.
97
- */
98
- const carriedNumberingProvenance = (source, target, keep) => ({ numPrFromStyle: expectParagraphAttrs(keep?.has("numPr") ? target : source).numPrFromStyle });
99
- /**
100
88
  * The paragraph left once the break of the paragraph at `paragraphPos` goes:
101
89
  * the next one, or, when its own break is pending deletion too, the first
102
90
  * paragraph after it whose break stays. Null when no paragraph follows.
@@ -143,16 +131,4 @@ const recordReplacedParagraphProperties = ({ tr, position, replaced, revision })
143
131
  }]);
144
132
  return true;
145
133
  };
146
- /**
147
- * The properties a batch's own property change set on a paragraph: the ones
148
- * whose value differs from what that change records as before.
149
- */
150
- const propertiesSetInBatch = (node, batchRevisionIds) => {
151
- const changes = expectParagraphAttrs(node)._propertyChanges;
152
- const change = Array.isArray(changes) ? changes.find(({ info }) => batchRevisionIds.has(info.id)) : void 0;
153
- if (change === void 0) return /* @__PURE__ */ new Set();
154
- const before = change.previousFormatting ?? {};
155
- const now = paragraphPropertiesSnapshot(node);
156
- return new Set([.../* @__PURE__ */ new Set([...Object.keys(before), ...Object.keys(now)])].filter((key) => JSON.stringify(before[key]) !== JSON.stringify(now[key])));
157
- };
158
- export { carriedParagraphProperties, carryParagraphProperties, paragraphLeftAfter, propertiesSetInBatch, recordReplacedParagraphProperties };
134
+ export { carryParagraphProperties, paragraphLeftAfter, recordReplacedParagraphProperties };
@@ -531,6 +531,17 @@ function applySuggestionInsert(view, from, to, text, pluginState) {
531
531
  if (selectsAll) {
532
532
  from = Math.max(from, Selection.atStart(view.state.doc).from);
533
533
  to = Math.min(to, Selection.atEnd(view.state.doc).to);
534
+ } else if (typesOverSelection && selection instanceof CellSelection) {
535
+ const cellDeletion = view.state.schema.marks["deletion"];
536
+ if (cellDeletion) {
537
+ const ranges = selection.ranges.toSorted((a, b) => b.$from.pos - a.$from.pos);
538
+ for (const range of ranges) markRangeAsDeleted(tr, view.state.doc, range.$from.pos, range.$to.pos, cellDeletion, pluginState, view.state);
539
+ }
540
+ const last = Selection.findFrom(view.state.doc.resolve(to), -1, true);
541
+ if (last && last.to >= from) {
542
+ from = last.to;
543
+ to = last.to;
544
+ }
534
545
  }
535
546
  const replacedFormatting = selectsAll ? (view.state.doc.resolve(from).nodeAfter?.marks ?? []).filter(({ type }) => RUN_FORMATTING_MARK_NAMES.has(type.name)) : null;
536
547
  const insertAttrs = findAdjacentRevision(view.state.doc, from, "insertion", pluginState.author) || makeMarkAttrs(pluginState);
@@ -545,16 +556,18 @@ function applySuggestionInsert(view, from, to, text, pluginState) {
545
556
  to
546
557
  });
547
558
  if (formattingMarks !== void 0) tr.setStoredMarks(formattingMarks);
559
+ const typedFrom = tr.doc.resolve(insertAt).parent.inlineContent ? insertAt : insertAt + 1;
560
+ const typedTo = typedFrom + text.length;
548
561
  tr.insertText(text, insertAt, insertAt);
549
562
  const deletionType = view.state.schema.marks["deletion"];
550
- if (deletionType) tr.removeMark(insertAt, insertAt + text.length, deletionType);
551
- tr.addMark(insertAt, insertAt + text.length, insertionType.create(insertAttrs));
563
+ if (deletionType) tr.removeMark(typedFrom, typedTo, deletionType);
564
+ tr.addMark(typedFrom, typedTo, insertionType.create(insertAttrs));
552
565
  if (replacedFormatting) {
553
- for (const mark of tr.doc.resolve(insertAt).nodeAfter?.marks ?? []) if (RUN_FORMATTING_MARK_NAMES.has(mark.type.name)) tr.removeMark(insertAt, insertAt + text.length, mark.type);
554
- for (const mark of replacedFormatting) tr.addMark(insertAt, insertAt + text.length, mark);
566
+ for (const mark of tr.doc.resolve(typedFrom).nodeAfter?.marks ?? []) if (RUN_FORMATTING_MARK_NAMES.has(mark.type.name)) tr.removeMark(typedFrom, typedTo, mark.type);
567
+ for (const mark of replacedFormatting) tr.addMark(typedFrom, typedTo, mark);
555
568
  }
556
- separateSplitStretch(tr, insertAt, insertAt + text.length, insertAttrs.revisionId);
557
- if (typesOverSelection) tr.setSelection(TextSelection.create(tr.doc, insertAt + text.length));
569
+ separateSplitStretch(tr, typedFrom, typedTo, insertAttrs.revisionId);
570
+ if (typesOverSelection) tr.setSelection(TextSelection.create(tr.doc, typedTo));
558
571
  view.dispatch(tr.scrollIntoView());
559
572
  return true;
560
573
  }
@@ -12,6 +12,7 @@ export declare const NON_INCLUSIVE_MARK_DISPOSITION: {
12
12
  readonly hyperlink: "carry";
13
13
  readonly footnoteRef: "followsTheText";
14
14
  readonly runIdentity: "followsTheText";
15
+ readonly hyphenElement: "followsTheText";
15
16
  readonly insertion: "followsTheText";
16
17
  readonly deletion: "followsTheText";
17
18
  readonly runPropertyChange: "followsTheText";
@@ -84,6 +84,7 @@ const NON_INCLUSIVE_MARK_DISPOSITION = {
84
84
  hyperlink: "carry",
85
85
  footnoteRef: "followsTheText",
86
86
  runIdentity: "followsTheText",
87
+ hyphenElement: "followsTheText",
87
88
  insertion: "followsTheText",
88
89
  deletion: "followsTheText",
89
90
  runPropertyChange: "followsTheText",
@@ -14,6 +14,14 @@ export type SectionBreakType = (typeof SECTION_BREAK_TYPES)[number];
14
14
  export declare const parseSectionBreakType: (value: unknown) => SectionBreakType | null;
15
15
  /** The section record a paragraph holds, or `null` when it ends no section. */
16
16
  export declare const sectionPropertiesOf: (paragraph: Node) => document_d_exports.SectionProperties | null;
17
+ /**
18
+ * The positions of paragraphs that hold only a copy of a section record: every
19
+ * carrier of a record but its last. The from-leg writes the record on the last
20
+ * carrier alone (`keepTheSectionBreakOnTheLastParagraphThatCarriesIt`), so a
21
+ * resolution that lets the last carrier's mark go must not leave a copy to
22
+ * inherit the section a saved package no longer has.
23
+ */
24
+ export declare const sectionRecordCopyPositions: (doc: Node) => number[];
17
25
  /**
18
26
  * The break type a section record states, or `null` when it states none.
19
27
  *
@@ -19,6 +19,34 @@ const parseSectionBreakType = (value) => isSectionBreakType(value) ? value : nul
19
19
  /** The section record a paragraph holds, or `null` when it ends no section. */
20
20
  const sectionPropertiesOf = (paragraph) => paragraph.attrs["_sectionProperties"] ?? null;
21
21
  /**
22
+ * The positions of paragraphs that hold only a copy of a section record: every
23
+ * carrier of a record but its last. The from-leg writes the record on the last
24
+ * carrier alone (`keepTheSectionBreakOnTheLastParagraphThatCarriesIt`), so a
25
+ * resolution that lets the last carrier's mark go must not leave a copy to
26
+ * inherit the section a saved package no longer has.
27
+ */
28
+ const sectionRecordCopyPositions = (doc) => {
29
+ const lastCarrier = /* @__PURE__ */ new Map();
30
+ const copies = [];
31
+ const visit = (parent, parentStart) => {
32
+ parent.forEach((node, offset) => {
33
+ const position = parentStart + offset;
34
+ if (!node.isTextblock) {
35
+ if (!node.isLeaf) visit(node, position + 1);
36
+ return;
37
+ }
38
+ if (node.type.name !== "paragraph") return;
39
+ const record = sectionPropertiesOf(node);
40
+ if (record === null) return;
41
+ const earlier = lastCarrier.get(record);
42
+ if (earlier !== void 0) copies.push(earlier);
43
+ lastCarrier.set(record, position);
44
+ });
45
+ };
46
+ visit(doc, 0);
47
+ return copies;
48
+ };
49
+ /**
22
50
  * The break type a section record states, or `null` when it states none.
23
51
  *
24
52
  * The single derivation every reader goes through. Every producer of a record
@@ -36,4 +64,4 @@ const sectionBreakTypeOf = (properties) => properties?.sectionStart ?? null;
36
64
  * package does with a `w:sectPr` that omits them.
37
65
  */
38
66
  const mintSectionProperties = (breakType) => ({ sectionStart: breakType });
39
- export { SECTION_BREAK_TYPES, mintSectionProperties, parseSectionBreakType, sectionBreakTypeOf, sectionPropertiesOf };
67
+ export { SECTION_BREAK_TYPES, mintSectionProperties, parseSectionBreakType, sectionBreakTypeOf, sectionPropertiesOf, sectionRecordCopyPositions };
@@ -13,6 +13,16 @@ export type ParagraphCascadeResolver = Pick<StyleEngine, "getStyle" | "getDocDef
13
13
  */
14
14
  type ParagraphDefaultFormattingResolver = Pick<StyleEngine, "getStyle" | "getDocDefaults" | "getDefaultParagraphStyle" | "getDefaultCharacterStyle" | "getRunStyleOwnProperties">;
15
15
  export declare function resolveRunFormattingWithoutDefaults(formatting: document_d_exports.TextFormatting | undefined, styleResolver: ParagraphDefaultFormattingResolver | null): document_d_exports.TextFormatting | undefined;
16
+ /**
17
+ * What a paragraph's mark lends its body runs: nothing when the paragraph has a
18
+ * style, and never the font or the mark-only decorations. One rule for a
19
+ * document read and for a paragraph whose properties a review restores.
20
+ */
21
+ export declare const inheritableParagraphMarkFormatting: ({ runProperties, styleId, styleResolver }: {
22
+ runProperties: document_d_exports.TextFormatting | undefined;
23
+ styleId: string | undefined;
24
+ styleResolver: Parameters<typeof resolveRunFormattingWithoutDefaults>[1];
25
+ }) => document_d_exports.TextFormatting | undefined;
16
26
  /** @internal Recompute a paragraph's inherited run defaults from authored package state. */
17
27
  export declare function resolveParagraphDefaultTextFormatting(styleId: string | undefined, formatting: document_d_exports.Paragraph["formatting"] | undefined, styleResolver: ParagraphDefaultFormattingResolver, options?: {
18
28
  includeParagraphMarkRunProperties?: boolean;
@@ -5,7 +5,7 @@ import { paragraphNumberingAttr } from "../numberingAttr.js";
5
5
  import { directionFromBidi } from "../paragraphDirection.js";
6
6
  import { styleResolvedParagraphFormatting } from "../paragraphFormattingProvenance.js";
7
7
  import { lineSpacingProvenanceFromSpacing } from "../paragraphSpacing.js";
8
- import { stripParagraphMarkOnlyFormatting } from "../runStyleFormatting.js";
8
+ import { stripParagraphMarkFormattingForBodyRuns, stripParagraphMarkOnlyFormatting } from "../runStyleFormatting.js";
9
9
  import { cascadeStyleTextFormatting } from "./styleToggleCascade.js";
10
10
  // src/prosemirror/styles/paragraphStyleCascade.ts
11
11
  /**
@@ -30,6 +30,16 @@ function resolveRunFormattingWithoutDefaults(formatting, styleResolver) {
30
30
  type: "direct"
31
31
  }]).formatting;
32
32
  }
33
+ /**
34
+ * What a paragraph's mark lends its body runs: nothing when the paragraph has a
35
+ * style, and never the font or the mark-only decorations. One rule for a
36
+ * document read and for a paragraph whose properties a review restores.
37
+ */
38
+ const inheritableParagraphMarkFormatting = ({ runProperties, styleId, styleResolver }) => {
39
+ if (styleId !== void 0) return void 0;
40
+ const formatting = resolveRunFormattingWithoutDefaults(runProperties, styleResolver);
41
+ return formatting ? stripParagraphMarkFormattingForBodyRuns(formatting) : void 0;
42
+ };
33
43
  /** @internal Recompute a paragraph's inherited run defaults from authored package state. */
34
44
  function resolveParagraphDefaultTextFormatting(styleId, formatting, styleResolver, options = {}) {
35
45
  const style = styleId ? styleResolver.getStyle(styleId) ?? styleResolver.getDefaultParagraphStyle() : styleResolver.getDefaultParagraphStyle();
@@ -155,4 +165,4 @@ function tableCellParagraphOverlay(styleResolver, tableStyleId, regions) {
155
165
  }
156
166
  return overlay;
157
167
  }
158
- export { extractTableParagraphOverlay, paragraphStyleCascadeAttrs, resolveParagraphDefaultTextFormatting, resolveRunFormattingWithoutDefaults, tableCellParagraphOverlay };
168
+ export { extractTableParagraphOverlay, inheritableParagraphMarkFormatting, paragraphStyleCascadeAttrs, resolveParagraphDefaultTextFormatting, resolveRunFormattingWithoutDefaults, tableCellParagraphOverlay };
@@ -1,20 +1,26 @@
1
1
  import { RunStyleResolver } from "./runStyleFormatting.js";
2
2
  import { ParagraphPropertySourceTransfer } from "../docx/paragraphPropertySource.js";
3
- import { MarkType, Node } from "prosemirror-model";
3
+ import { Node } from "prosemirror-model";
4
4
  import { StepMap, Transform } from "prosemirror-transform";
5
+ type ResolutionMode = "accept" | "reject";
5
6
  /**
6
7
  * Whether the cell a paragraph's words would run into is itself pending: its
7
8
  * row or the cell (through nested tables' first cells) is inserted, deleted
8
- * or merged, so resolving may not keep it. Pending rows or cells elsewhere in
9
- * the table leave that cell where it is.
9
+ * or merged, so resolving may not keep it. A deletion is not pending for a
10
+ * rejection of every change. Pending rows or cells elsewhere in the table leave
11
+ * that cell where it is.
10
12
  */
11
- export declare const runInCellPending: (table: Node) => boolean;
13
+ export declare const runInCellPending: (table: Node, everyChange?: ResolutionMode) => boolean;
12
14
  type RunParagraphIntoTableOptions = {
13
15
  tr: Transform;
14
16
  paragraphPos: number;
15
17
  styleResolver: RunStyleResolver | null;
16
- /** Content a resolution in progress removes: it does not count as words left. */
17
- removedMark?: MarkType | undefined;
18
+ /**
19
+ * Set when every change of the story resolves this way, not only some: what
20
+ * it removes does not count as words left, and a rejection takes a deleted
21
+ * row as staying.
22
+ */
23
+ everyChange?: ResolutionMode | undefined;
18
24
  };
19
25
  /**
20
26
  * Run the paragraph at `paragraphPos` on into the table right after it.
@@ -22,7 +28,7 @@ type RunParagraphIntoTableOptions = {
22
28
  * paragraph-property source it hands over, or null when it did not run in and
23
29
  * the caller keeps its own handling.
24
30
  */
25
- export declare const runParagraphIntoTable: ({ tr, paragraphPos, styleResolver, removedMark }: RunParagraphIntoTableOptions) => {
31
+ export declare const runParagraphIntoTable: ({ tr, paragraphPos, styleResolver, everyChange }: RunParagraphIntoTableOptions) => {
26
32
  position: number;
27
33
  map: StepMap;
28
34
  transfer: ParagraphPropertySourceTransfer;
@@ -34,8 +40,17 @@ export declare const runParagraphIntoTable: ({ tr, paragraphPos, styleResolver,
34
40
  type RunsIntoFollowingTableOptions = {
35
41
  parent: Node;
36
42
  index: number;
37
- mode: "accept" | "reject";
43
+ mode: ResolutionMode;
38
44
  };
45
+ /**
46
+ * Whether what stands right after a paragraph whose mark goes is itself removed
47
+ * by this resolution, so the paragraph runs on into what stands after that, as
48
+ * it would had the removed block never been there: a table it removes, or, when
49
+ * rejecting, an inserted paragraph it empties with a table after it. (Accepted,
50
+ * a deleted paragraph did stand there, and the join ended at it. Paragraphs join
51
+ * among themselves without help.)
52
+ */
53
+ export declare const paragraphBeforeRemovedBlock: ({ parent, index, mode }: RunsIntoFollowingTableOptions) => boolean;
39
54
  export declare const runsIntoFollowingTable: ({ parent, index, mode }: RunsIntoFollowingTableOptions) => boolean;
40
55
  /**
41
56
  * Before every change in a story is resolved: run each paragraph whose mark
@@ -43,7 +58,7 @@ export declare const runsIntoFollowingTable: ({ parent, index, mode }: RunsIntoF
43
58
  * paragraph that took words (its position after the step count given) and
44
59
  * the paragraph-property sources handed over.
45
60
  */
46
- export declare const runParagraphsIntoTables: (tr: Transform, mode: "accept" | "reject", styleResolver: RunStyleResolver | null) => {
61
+ export declare const runParagraphsIntoTables: (tr: Transform, mode: ResolutionMode, styleResolver: RunStyleResolver | null) => {
47
62
  targets: {
48
63
  position: number;
49
64
  step: number;