@stll/folio-core 0.60.0 → 0.62.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.
Files changed (173) hide show
  1. package/README.md +13 -0
  2. package/dist/ai-edits/apply.js +139 -32
  3. package/dist/ai-edits/headless.d.ts +3 -0
  4. package/dist/ai-edits/headless.js +70 -75
  5. package/dist/ai-edits/note-story-inventory.d.ts +7 -0
  6. package/dist/ai-edits/note-story-inventory.js +11 -0
  7. package/dist/ai-edits/read.js +16 -9
  8. package/dist/ai-edits/revisionStretches.js +52 -1
  9. package/dist/ai-edits/table-row-column-mutations.js +6 -1
  10. package/dist/compare/reproducible-package.js +2 -1
  11. package/dist/compare/verification.js +1 -0
  12. package/dist/compat/eigenpal.d.ts +3 -1
  13. package/dist/controller/canonicalClipboard.js +2 -1
  14. package/dist/controller/canonicalListRendering.d.ts +4 -6
  15. package/dist/controller/canonicalListRendering.js +3 -157
  16. package/dist/controller/headerFooterEditorManager.d.ts +1 -0
  17. package/dist/controller/headerFooterEditorManager.js +11 -0
  18. package/dist/controller/hiddenEditorManager.d.ts +1 -1
  19. package/dist/controller/hiddenEditorManager.js +3 -1
  20. package/dist/controller/layoutPipeline.d.ts +3 -1
  21. package/dist/controller/layoutPipeline.js +13 -4
  22. package/dist/controller/layoutSession.d.ts +2 -1
  23. package/dist/controller/noteEditorManager.d.ts +1 -1
  24. package/dist/display-list/build/imagePrimitives.js +28 -12
  25. package/dist/display-list/build/paragraphPrimitives.js +15 -2
  26. package/dist/display-list/dom/renderDisplayListToDom.js +8 -5
  27. package/dist/display-list/types.d.ts +7 -3
  28. package/dist/docx/canonicalStoryRepack.js +3 -2
  29. package/dist/docx/commentTextParagraph.d.ts +6 -0
  30. package/dist/docx/commentTextParagraph.js +15 -0
  31. package/dist/docx/conformance.js +6 -1
  32. package/dist/docx/diagramPreview.js +2 -1
  33. package/dist/docx/documentParser.d.ts +13 -1
  34. package/dist/docx/documentParser.js +1 -1
  35. package/dist/docx/encryption/compoundFile.d.ts +0 -8
  36. package/dist/docx/encryption/compoundFile.js +2 -1
  37. package/dist/docx/encryption/cryptoBytes.d.ts +2 -2
  38. package/dist/docx/encryption/cryptoBytes.js +3 -18
  39. package/dist/docx/encryption/encryptionInfo.d.ts +0 -5
  40. package/dist/docx/encryption/encryptionInfo.js +2 -1
  41. package/dist/docx/ensureParaIds.js +2 -1
  42. package/dist/docx/groupDrawingParser.js +28 -13
  43. package/dist/docx/listModelNormalization.d.ts +10 -0
  44. package/dist/docx/listModelNormalization.js +166 -0
  45. package/dist/docx/metadataPrivacy.js +3 -2
  46. package/dist/docx/metafileRaster.js +2 -1
  47. package/dist/docx/packageParts.js +6 -5
  48. package/dist/docx/paragraphParser.js +6 -4
  49. package/dist/docx/parser.js +5 -4
  50. package/dist/docx/preservedRunContent.js +11 -2
  51. package/dist/docx/removeHeaderFooterParts.js +8 -7
  52. package/dist/docx/replyToComment.js +3 -13
  53. package/dist/docx/rezip.js +88 -89
  54. package/dist/docx/runParser.js +12 -6
  55. package/dist/docx/runPropertyPresence.d.ts +11 -0
  56. package/dist/docx/runPropertyPresence.js +14 -0
  57. package/dist/docx/selectiveSave.js +15 -14
  58. package/dist/docx/serializer/runSerializer.js +3 -2
  59. package/dist/docx/serializer/tableSerializer.js +2 -1
  60. package/dist/docx/server/applyDocxXmlPatchProposal.js +2 -2
  61. package/dist/docx/server/boundedArchive.js +2 -1
  62. package/dist/docx/server/evaluateDocxXmlPatchProposal.js +2 -1
  63. package/dist/docx/server/inspectDocxPackage.js +5 -1
  64. package/dist/docx/server/validateDocxConformance.js +2 -1
  65. package/dist/docx/tableParser.js +7 -4
  66. package/dist/docx/tableRowPropertyPresence.d.ts +9 -0
  67. package/dist/docx/tableRowPropertyPresence.js +15 -0
  68. package/dist/docx/unzip.js +2 -1
  69. package/dist/docx/xmlEncoding.js +6 -5
  70. package/dist/fonts/fontsourceFaces.js +2 -1
  71. package/dist/fonts/sfnt/parse.js +6 -9
  72. package/dist/fonts/sfnt/subset.js +2 -1
  73. package/dist/fonts/sfnt/tables.js +2 -3
  74. package/dist/fonts/sfnt/woff.js +2 -1
  75. package/dist/i18n/messages/ar.gen.js +1 -0
  76. package/dist/i18n/messages/catalogs.gen.d.ts +17 -0
  77. package/dist/i18n/messages/catalogs.gen.js +17 -0
  78. package/dist/i18n/messages/cs.gen.js +1 -0
  79. package/dist/i18n/messages/de.gen.js +1 -0
  80. package/dist/i18n/messages/en.gen.js +1 -0
  81. package/dist/i18n/messages/es.gen.js +1 -0
  82. package/dist/i18n/messages/et.gen.js +1 -0
  83. package/dist/i18n/messages/fr.gen.js +1 -0
  84. package/dist/i18n/messages/he.gen.js +1 -0
  85. package/dist/i18n/messages/hi.gen.js +1 -0
  86. package/dist/i18n/messages/hu.gen.js +1 -0
  87. package/dist/i18n/messages/lt.gen.js +1 -0
  88. package/dist/i18n/messages/lv.gen.js +1 -0
  89. package/dist/i18n/messages/messages.gen.d.ts +1 -0
  90. package/dist/i18n/messages/pl.gen.js +1 -0
  91. package/dist/i18n/messages/pt-BR.gen.js +1 -0
  92. package/dist/i18n/messages/sk.gen.js +1 -0
  93. package/dist/i18n/messages/tr.gen.js +1 -0
  94. package/dist/i18n/messages/zh-CN.gen.js +1 -0
  95. package/dist/index.d.ts +3 -1
  96. package/dist/internal/revisionResolutionInline.d.ts +2 -1
  97. package/dist/internal/revisionResolutionInline.js +22 -1
  98. package/dist/internal/wholeStoryRevisionResolution.js +24 -7
  99. package/dist/layout-bridge/convert/endnoteLayout.d.ts +1 -1
  100. package/dist/layout-bridge/convert/endnoteLayout.js +1 -3
  101. package/dist/layout-bridge/convert/flowConversionShared.d.ts +2 -0
  102. package/dist/layout-bridge/convert/footnoteLayout.d.ts +4 -0
  103. package/dist/layout-bridge/convert/footnoteLayout.js +13 -3
  104. package/dist/layout-bridge/convert/headerFooterLayout.d.ts +5 -0
  105. package/dist/layout-bridge/convert/headerFooterLayout.js +12 -15
  106. package/dist/layout-bridge/convert/paragraphRuns.js +2 -1
  107. package/dist/layout-engine/index.js +2 -2
  108. package/dist/layout-engine/measure/crossRunWords.js +12 -2
  109. package/dist/layout-engine/noteAreaFlow.d.ts +4 -4
  110. package/dist/layout-engine/noteAreaFlow.js +12 -6
  111. package/dist/layout-engine/types.d.ts +5 -2
  112. package/dist/layout-painter/index.d.ts +1 -1
  113. package/dist/managers/AutoSaveManager.js +2 -1
  114. package/dist/managers/EditorModeManager.d.ts +10 -1
  115. package/dist/managers/EditorModeManager.js +15 -1
  116. package/dist/managers/FindReplaceManager.d.ts +13 -6
  117. package/dist/managers/FindReplaceManager.js +15 -9
  118. package/dist/managers/TableSelectionManager.d.ts +1 -1
  119. package/dist/managers/autoSaveCodec.js +2 -6
  120. package/dist/managers/reviewDisplayMode.d.ts +8 -0
  121. package/dist/managers/reviewDisplayMode.js +15 -0
  122. package/dist/paged-layout/editorScrollRoot.d.ts +3 -1
  123. package/dist/paged-layout/editorScrollRoot.js +2 -2
  124. package/dist/paged-layout/pageText.d.ts +11 -2
  125. package/dist/paged-layout/pageText.js +8 -3
  126. package/dist/pdf/fonts.js +5 -14
  127. package/dist/pdf/images.js +4 -2
  128. package/dist/pdf/objects.d.ts +0 -11
  129. package/dist/pdf/objects.js +7 -9
  130. package/dist/pdf/writePdf.js +2 -1
  131. package/dist/prosemirror/commands/comments.js +51 -13
  132. package/dist/prosemirror/commentRangeInlineAtoms.d.ts +40 -0
  133. package/dist/prosemirror/commentRangeInlineAtoms.js +33 -0
  134. package/dist/prosemirror/containerFinalParagraph.d.ts +8 -8
  135. package/dist/prosemirror/containerFinalParagraph.js +8 -8
  136. package/dist/prosemirror/conversion/fromProseDoc.js +14 -8
  137. package/dist/prosemirror/conversion/toProseDoc.js +16 -23
  138. package/dist/prosemirror/extensions/nodes/CommentReferenceExtension.js +16 -2
  139. package/dist/prosemirror/fieldRepresentation.js +4 -2
  140. package/dist/prosemirror/findReplaceSelection.d.ts +48 -2
  141. package/dist/prosemirror/findReplaceSelection.js +153 -38
  142. package/dist/prosemirror/inlineDisplayText.d.ts +10 -0
  143. package/dist/prosemirror/inlineDisplayText.js +29 -0
  144. package/dist/prosemirror/markupViewProjection.d.ts +10 -1
  145. package/dist/prosemirror/markupViewProjection.js +21 -3
  146. package/dist/prosemirror/plugins/transactionInvariants.js +6 -1
  147. package/dist/prosemirror/replacedAnnotations.d.ts +15 -1
  148. package/dist/prosemirror/replacedAnnotations.js +71 -12
  149. package/dist/prosemirror/runFormattingReconciliation.d.ts +3 -1
  150. package/dist/prosemirror/runFormattingReconciliation.js +10 -2
  151. package/dist/prosemirror/runIdentityAcrossRevisions.d.ts +5 -7
  152. package/dist/prosemirror/runIdentityAcrossRevisions.js +66 -38
  153. package/dist/prosemirror/runPropertyChangeResolution.js +13 -1
  154. package/dist/prosemirror/trackedRunInlineAtoms.d.ts +1 -1
  155. package/dist/prosemirror/trackedRunInlineAtoms.js +1 -1
  156. package/dist/utils/base64.d.ts +1 -31
  157. package/dist/utils/base64.js +19 -33
  158. package/dist/utils/clipboard.js +5 -4
  159. package/dist/utils/colorResolver.js +11 -2
  160. package/dist/utils/findMatchView.d.ts +32 -0
  161. package/dist/utils/findMatchView.js +406 -0
  162. package/dist/utils/findReplace.d.ts +67 -4
  163. package/dist/utils/findReplace.js +359 -37
  164. package/dist/utils/findSearchKey.d.ts +29 -0
  165. package/dist/utils/findSearchKey.js +34 -0
  166. package/dist/utils/replaceText.js +138 -94
  167. package/dist/utils/sliceText.d.ts +2 -0
  168. package/dist/utils/sliceText.js +2 -0
  169. package/dist/utils/textEncoding.d.ts +2 -0
  170. package/dist/utils/textEncoding.js +2 -0
  171. package/dist/utils/tiffConverter.d.ts +0 -9
  172. package/dist/utils/tiffConverter.js +2 -4
  173. package/package.json +51 -25
@@ -1,3 +1,4 @@
1
+ import { isCommentRangeInertAtom } from "../../commentRangeInlineAtoms.js";
1
2
  import { readCommentReferenceAttrs } from "../../commentReferenceAttrs.js";
2
3
  import { planCommentReferenceRepairs } from "../../commentReferenceIntegrity.js";
3
4
  import { readRangeAnchorAttrs } from "../../rangeAnchorAttrs.js";
@@ -10,7 +11,16 @@ const COMMENT_REFERENCE_NODE_NAME = "commentReference";
10
11
  const surveyCommentReferences = (doc) => {
11
12
  const references = [];
12
13
  const markEnds = /* @__PURE__ */ new Map();
14
+ const unanchoredMarks = [];
13
15
  doc.descendants((node, position) => {
16
+ const inert = isCommentRangeInertAtom(node);
17
+ if (inert) {
18
+ for (const mark of node.marks) if (mark.type.name === "comment") unanchoredMarks.push({
19
+ position,
20
+ size: node.nodeSize,
21
+ mark
22
+ });
23
+ }
14
24
  if (node.type.name === "commentReference") {
15
25
  const attrs = readCommentReferenceAttrs(node);
16
26
  references.push(attrs.ok ? {
@@ -33,6 +43,7 @@ const surveyCommentReferences = (doc) => {
33
43
  }
34
44
  return false;
35
45
  }
46
+ if (inert) return false;
36
47
  if (!node.isInline) return true;
37
48
  for (const mark of node.marks) {
38
49
  if (mark.type.name !== "comment") continue;
@@ -44,6 +55,7 @@ const surveyCommentReferences = (doc) => {
44
55
  });
45
56
  return {
46
57
  references,
58
+ unanchoredMarks,
47
59
  markExtents: [...markEnds].map(([commentId, end]) => ({
48
60
  commentId,
49
61
  end
@@ -84,9 +96,11 @@ const CommentReferenceExtension = createNodeExtension({
84
96
  if (!transactions.some(({ docChanged }) => docChanged)) return null;
85
97
  const referenceType = newState.schema.nodes[COMMENT_REFERENCE_NODE_NAME];
86
98
  if (!referenceType) return null;
87
- const repairs = planCommentReferenceRepairs(surveyCommentReferences(newState.doc));
88
- if (repairs.length === 0) return null;
99
+ const survey = surveyCommentReferences(newState.doc);
100
+ const repairs = planCommentReferenceRepairs(survey);
101
+ if (repairs.length === 0 && survey.unanchoredMarks.length === 0) return null;
89
102
  const transaction = newState.tr;
103
+ for (const { position, size, mark } of survey.unanchoredMarks) transaction.removeMark(position, position + size, mark);
90
104
  for (const repair of repairs) {
91
105
  if (repair.type === "delete") {
92
106
  transaction.delete(repair.position, repair.position + repair.size);
@@ -1,15 +1,17 @@
1
1
  import "./extensions/marks/InlineWrapperExtension.js";
2
+ import { getInlineDisplayText } from "./inlineDisplayText.js";
2
3
  //#region src/prosemirror/fieldRepresentation.ts
3
4
  /** Current children alone decide whether a field needs editable structural content. */
4
5
  const fieldRequiresStructuredContent = (children) => children.some((child) => !child.isText || child.marks.some((mark) => mark.type.name === "hyperlink" || mark.type.name === "inlineWrapper" || mark.type.name === "insertion" || mark.type.name === "deletion"));
5
6
  /** Resolution returns text-only fields to the same atom representation as initial conversion. */
6
7
  const canonicalFieldNode = (node) => {
7
8
  if (node.type.name !== "structuredField") return node;
9
+ const displayText = getInlineDisplayText(node);
8
10
  const attrs = {
9
11
  ...node.attrs,
10
- displayText: node.textContent
12
+ displayText
11
13
  };
12
- if (fieldRequiresStructuredContent(node.content.content)) return node.attrs["displayText"] === node.textContent ? node : node.type.create(attrs, node.content, node.marks);
14
+ if (fieldRequiresStructuredContent(node.content.content)) return node.attrs["displayText"] === displayText ? node : node.type.create(attrs, node.content, node.marks);
13
15
  let marks = node.marks;
14
16
  for (const mark of node.firstChild?.marks ?? []) marks = mark.addToSet(marks);
15
17
  return node.type.schema.node("field", attrs, void 0, marks);
@@ -1,5 +1,8 @@
1
- import { FindOptions } from "../utils/findReplace.js";
1
+ import { document_d_exports } from "../types/document.js";
2
+ import { DisplayMode } from "../managers/EditorModeManager.js";
3
+ import { FindMatch, FindOptions, FindSearchContext } from "../utils/findReplace.js";
2
4
  import { Node } from "prosemirror-model";
5
+ import { EditorState } from "prosemirror-state";
3
6
  //#region src/prosemirror/findReplaceSelection.d.ts
4
7
  /**
5
8
  * Positional fields of a document search match that the range resolver needs.
@@ -20,11 +23,54 @@ type FindMatchPosition = {
20
23
  type FindMatchRange = {
21
24
  from: number;
22
25
  to: number;
26
+ /** Navigation anchors inside a logical match are excluded from decorations. */
27
+ excludedRanges?: {
28
+ from: number;
29
+ to: number;
30
+ }[];
31
+ /** Character subranges inside source atoms, whose text exceeds their PM size. */
32
+ atomicTextRanges?: {
33
+ from: number;
34
+ to: number;
35
+ startOffset: number;
36
+ endOffset: number;
37
+ }[];
23
38
  };
24
39
  type ProseMirrorFindMatch = FindMatchPosition & FindMatchRange & {
25
40
  text: string;
26
41
  };
27
42
  declare function resolveFindMatchRange(doc: Node, match: FindMatchPosition): FindMatchRange | null;
43
+ /** Resolve a result set in one paragraph traversal, preserving result order. */
44
+ declare const resolveFindMatchRanges: (doc: Node, matches: readonly FindMatchPosition[]) => (FindMatchRange | null)[];
28
45
  declare function findInProseMirrorDocument(doc: Node, searchText: string, options: FindOptions): ProseMirrorFindMatch[];
46
+ type FindInMarkupViewOptions = {
47
+ state: EditorState;
48
+ markupView: DisplayMode;
49
+ searchText: string;
50
+ options: FindOptions;
51
+ };
52
+ /** Search the displayed story; returned ranges address the authored editor document. */
53
+ declare const findInMarkupView: ({ state, markupView, searchText, options }: FindInMarkupViewOptions) => ProseMirrorFindMatch[];
54
+ type FindInDisplayedDocumentOptions = {
55
+ document: document_d_exports.Document;
56
+ /**
57
+ * Live body editor state. A review view reads its body from this state's
58
+ * projection, so null defers that search until the body editor mounts.
59
+ * Hosts without an editor state omit it and search the package model.
60
+ */
61
+ state?: EditorState | null | undefined;
62
+ searchText: string;
63
+ options: FindOptions;
64
+ context?: FindSearchContext | undefined;
65
+ };
66
+ /**
67
+ * Search every story a display mode shows. All Markup searches the package
68
+ * stories. A resolved review view searches its body through the projection the
69
+ * layout paints, so body ranges address the authored editor document; anchored
70
+ * shapes, headers, footers, notes and comments keep the package search owner
71
+ * with the same display mode. Returns null while a review view has no body
72
+ * editor state to project.
73
+ */
74
+ declare const findInDisplayedDocument: ({ document, state, searchText, options, context }: FindInDisplayedDocumentOptions) => FindMatch[] | null;
29
75
  //#endregion
30
- export { FindMatchPosition, FindMatchRange, ProseMirrorFindMatch, findInProseMirrorDocument, resolveFindMatchRange };
76
+ export { FindMatchPosition, FindMatchRange, ProseMirrorFindMatch, findInDisplayedDocument, findInMarkupView, findInProseMirrorDocument, resolveFindMatchRange, resolveFindMatchRanges };
@@ -1,6 +1,7 @@
1
- import { findAllMatches } from "../utils/findReplace.js";
2
- import { decodeOoxmlSymbolCharacter } from "../utils/ooxmlSymbol.js";
3
- import { expectSymbolAttrs } from "./attrs/index.js";
1
+ import { findAllMatches, findInDocument, isPageDependentSearchField } from "../utils/findReplace.js";
2
+ import { expectFieldAttrs } from "./attrs/index.js";
3
+ import { getInlineDisplayText, getInlineDisplayToken, isNoteReferenceText } from "./inlineDisplayText.js";
4
+ import { projectMarkupView } from "./markupViewProjection.js";
4
5
  //#region src/prosemirror/findReplaceSelection.ts
5
6
  function resolveFindMatchRange(doc, match) {
6
7
  let resolved = null;
@@ -16,11 +17,29 @@ function resolveFindMatchRange(doc, match) {
16
17
  });
17
18
  return resolved;
18
19
  }
20
+ /** Resolve a result set in one paragraph traversal, preserving result order. */
21
+ const resolveFindMatchRanges = (doc, matches) => {
22
+ const paragraphs = /* @__PURE__ */ new Map();
23
+ forEachSearchParagraph(doc, (paragraph, paragraphPos, paragraphIndex) => {
24
+ paragraphs.set(paragraphIndex, {
25
+ paragraph,
26
+ paragraphPos
27
+ });
28
+ return true;
29
+ });
30
+ return matches.map((match) => {
31
+ const paragraph = paragraphs.get(match.paragraphIndex);
32
+ return paragraph ? resolveTextRangeInParagraph({
33
+ ...paragraph,
34
+ ...match
35
+ }) : null;
36
+ });
37
+ };
19
38
  function findInProseMirrorDocument(doc, searchText, options) {
20
39
  if (!searchText) return [];
21
40
  const matches = [];
22
41
  forEachSearchParagraph(doc, (paragraph, paragraphPos, paragraphIndex) => {
23
- const text = getSearchableParagraphText(paragraph);
42
+ const text = getInlineDisplayText(paragraph);
24
43
  for (const { start, end } of findAllMatches(text, searchText, options)) {
25
44
  const range = resolveTextRangeInParagraph({
26
45
  paragraph,
@@ -40,6 +59,100 @@ function findInProseMirrorDocument(doc, searchText, options) {
40
59
  });
41
60
  return matches;
42
61
  }
62
+ /** Search the displayed story; returned ranges address the authored editor document. */
63
+ const findInMarkupView = ({ state, markupView, searchText, options }) => {
64
+ const projection = projectMarkupView(state, markupView);
65
+ if (projection.type === "authored") return findInProseMirrorDocument(state.doc, searchText, options);
66
+ return findInProseMirrorDocument(projection.doc, searchText, options).map((match) => toAuthoredFindMatch(projection, match));
67
+ };
68
+ /** Every view-document position a match carries, mapped to the authored editor document. */
69
+ const toAuthoredFindMatch = (projection, match) => {
70
+ const toEditorRange = ({ from, to }) => ({
71
+ from: projection.toEditor(from, 1),
72
+ to: projection.toEditor(to, -1)
73
+ });
74
+ return {
75
+ paragraphIndex: match.paragraphIndex,
76
+ startOffset: match.startOffset,
77
+ endOffset: match.endOffset,
78
+ text: match.text,
79
+ ...toEditorRange(match),
80
+ ...match.excludedRanges && { excludedRanges: match.excludedRanges.map(toEditorRange) },
81
+ ...match.atomicTextRanges && { atomicTextRanges: match.atomicTextRanges.map((atom) => ({
82
+ ...toEditorRange(atom),
83
+ startOffset: atom.startOffset,
84
+ endOffset: atom.endOffset
85
+ })) }
86
+ };
87
+ };
88
+ /** Replacement policy for a projected body match, in the shared eligibility vocabulary. */
89
+ const projectedReplacement = (state, viewMatch, authored) => {
90
+ const atom = authored.atomicTextRanges?.at(0);
91
+ if (atom) return {
92
+ status: "blocked",
93
+ reason: state.doc.nodeAt(atom.from)?.type.name === "field" ? "field-result" : "atomic-content"
94
+ };
95
+ return authored.to - authored.from === viewMatch.to - viewMatch.from ? { status: "allowed" } : {
96
+ status: "blocked",
97
+ reason: "hidden-content"
98
+ };
99
+ };
100
+ /**
101
+ * Search every story a display mode shows. All Markup searches the package
102
+ * stories. A resolved review view searches its body through the projection the
103
+ * layout paints, so body ranges address the authored editor document; anchored
104
+ * shapes, headers, footers, notes and comments keep the package search owner
105
+ * with the same display mode. Returns null while a review view has no body
106
+ * editor state to project.
107
+ */
108
+ const findInDisplayedDocument = ({ document, state, searchText, options, context }) => {
109
+ const displayMode = options.displayMode ?? "all-markup";
110
+ if (displayMode === "all-markup" || state === void 0) return findInDocument(document, searchText, options, context);
111
+ if (state === null) return null;
112
+ const packageMatches = findInDocument(document, searchText, options, context);
113
+ const projection = projectMarkupView(state, displayMode);
114
+ if (projection.type === "authored" || !searchText) return packageMatches;
115
+ const bodyMatches = findInProseMirrorDocument(projection.doc, searchText, options).map((viewMatch) => {
116
+ const authored = toAuthoredFindMatch(projection, viewMatch);
117
+ const { from, to, excludedRanges, atomicTextRanges } = authored;
118
+ return {
119
+ paragraphIndex: authored.paragraphIndex,
120
+ contentIndex: 0,
121
+ startOffset: authored.startOffset,
122
+ endOffset: authored.endOffset,
123
+ text: authored.text,
124
+ story: "main",
125
+ replacement: projectedReplacement(state, viewMatch, authored),
126
+ authoredRange: {
127
+ from,
128
+ to,
129
+ ...excludedRanges && { excludedRanges },
130
+ ...atomicTextRanges && { atomicTextRanges }
131
+ }
132
+ };
133
+ });
134
+ const anchorEnds = [];
135
+ forEachSearchParagraph(state.doc, (paragraph, paragraphPos, paragraphIndex) => {
136
+ anchorEnds[paragraphIndex] = paragraphPos + paragraph.nodeSize;
137
+ return true;
138
+ });
139
+ const bodyShapeMatches = packageMatches.filter((match) => match.story === "main" && match.textBoxIndex !== void 0);
140
+ const orderedBody = [];
141
+ let shapeIndex = 0;
142
+ const shapeAnchorEnd = (match) => match.anchorParagraphIndex !== void 0 && anchorEnds[match.anchorParagraphIndex] || Number.POSITIVE_INFINITY;
143
+ for (const bodyMatch of bodyMatches) {
144
+ const from = bodyMatch.authoredRange?.from ?? 0;
145
+ while (shapeIndex < bodyShapeMatches.length) {
146
+ const shapeMatch = bodyShapeMatches[shapeIndex];
147
+ if (!shapeMatch || shapeAnchorEnd(shapeMatch) > from) break;
148
+ orderedBody.push(shapeMatch);
149
+ shapeIndex++;
150
+ }
151
+ orderedBody.push(bodyMatch);
152
+ }
153
+ orderedBody.push(...bodyShapeMatches.slice(shapeIndex));
154
+ return [...orderedBody, ...packageMatches.filter((match) => match.story !== "main" || match.textBoxIndex === void 0 && match.commentId !== void 0)];
155
+ };
43
156
  function forEachSearchParagraph(doc, visit) {
44
157
  let paragraphIndex = 0;
45
158
  const walkBlocks = (container, contentStart) => {
@@ -89,54 +202,56 @@ function resolveTextRangeInParagraph({ paragraph, paragraphPos, startOffset, end
89
202
  to: null
90
203
  };
91
204
  const pageBreakOffsets = [];
205
+ const excludedTextRanges = [];
206
+ const excludedRanges = [];
207
+ const atomicTextRanges = [];
92
208
  paragraph.descendants((node, pos) => {
209
+ if ((node.type.name === "field" || node.type.name === "structuredField") && isPageDependentSearchField(expectFieldAttrs(node).instruction)) {
210
+ const length = node.type.name === "field" ? expectFieldAttrs(node).displayText.length : getInlineDisplayText(node).length;
211
+ excludedTextRanges.push({
212
+ start: textOffset,
213
+ end: textOffset + length
214
+ });
215
+ }
93
216
  if (node.type.name === "pageBreakRun") {
94
217
  pageBreakOffsets.push(textOffset);
95
218
  return false;
96
219
  }
220
+ if (isNoteReferenceText(node)) {
221
+ const from = paragraphPos + 1 + pos;
222
+ excludedRanges.push({
223
+ from,
224
+ to: from + node.nodeSize
225
+ });
226
+ }
97
227
  const tokenLength = getSearchTextTokenLength(node);
98
228
  if (tokenLength === 0) return true;
99
229
  const textStart = textOffset;
100
230
  const textEnd = textStart + tokenLength;
101
231
  const nodeStart = paragraphPos + 1 + pos;
102
- if (range.from === null && startOffset >= textStart && startOffset < textEnd) range.from = nodeStart + Math.min(startOffset - textStart, node.nodeSize);
103
- if (range.to === null && endOffset > textStart && endOffset <= textEnd) range.to = nodeStart + Math.min(endOffset - textStart, node.nodeSize);
232
+ const atomic = node.type.name === "field" || node.type.name === "preservedXml";
233
+ if (atomic && textStart < endOffset && textEnd > startOffset) atomicTextRanges.push({
234
+ from: nodeStart,
235
+ to: nodeStart + node.nodeSize,
236
+ startOffset: Math.max(0, startOffset - textStart),
237
+ endOffset: Math.min(tokenLength, endOffset - textStart)
238
+ });
239
+ if (range.from === null && startOffset >= textStart && startOffset < textEnd) range.from = atomic ? nodeStart : nodeStart + Math.min(startOffset - textStart, node.nodeSize);
240
+ if (range.to === null && endOffset > textStart && endOffset <= textEnd) range.to = atomic ? nodeStart + node.nodeSize : nodeStart + Math.min(endOffset - textStart, node.nodeSize);
104
241
  textOffset = textEnd;
105
242
  return range.to === null;
106
243
  });
107
- if (range.from === null || range.to === null || range.from >= range.to || pageBreakOffsets.some((offset) => offset > startOffset && offset < endOffset)) return null;
244
+ if (range.from === null || range.to === null || range.from >= range.to || pageBreakOffsets.some((offset) => offset > startOffset && offset < endOffset) || excludedTextRanges.some(({ start, end }) => start < endOffset && end > startOffset)) return null;
245
+ const from = range.from;
246
+ const to = range.to;
247
+ const visibleExcludedRanges = excludedRanges.filter((anchor) => anchor.from < to && anchor.to > from);
108
248
  return {
109
- from: range.from,
110
- to: range.to
249
+ from,
250
+ to,
251
+ ...atomicTextRanges.length > 0 && { atomicTextRanges },
252
+ ...visibleExcludedRanges.length > 0 && { excludedRanges: visibleExcludedRanges }
111
253
  };
112
254
  }
113
- function getSearchTextTokenLength(node) {
114
- if (node.isText) return node.text?.length ?? 0;
115
- if (node.type.name === "tab" || node.type.name === "hardBreak" || node.type.name === "symbol") return 1;
116
- return 0;
117
- }
118
- function getSearchableParagraphText(paragraph) {
119
- let text = "";
120
- paragraph.descendants((node) => {
121
- if (node.isText) {
122
- text += node.text ?? "";
123
- return false;
124
- }
125
- if (node.type.name === "tab") {
126
- text += " ";
127
- return false;
128
- }
129
- if (node.type.name === "hardBreak") {
130
- text += "\n";
131
- return false;
132
- }
133
- if (node.type.name === "symbol") {
134
- text += decodeOoxmlSymbolCharacter(expectSymbolAttrs(node).char) ?? "";
135
- return false;
136
- }
137
- return true;
138
- });
139
- return text;
140
- }
255
+ const getSearchTextTokenLength = (node) => getInlineDisplayToken(node)?.length ?? 0;
141
256
  //#endregion
142
- export { findInProseMirrorDocument, resolveFindMatchRange };
257
+ export { findInDisplayedDocument, findInMarkupView, findInProseMirrorDocument, resolveFindMatchRange, resolveFindMatchRanges };
@@ -0,0 +1,10 @@
1
+ import { Node } from "prosemirror-model";
2
+ //#region src/prosemirror/inlineDisplayText.d.ts
3
+ /** Native note-reference labels are navigation anchors, outside searchable text. */
4
+ declare const isNoteReferenceText: (node: Node) => boolean;
5
+ /** A leaf's logical text; null means a transparent container to descend into. */
6
+ declare const getInlineDisplayToken: (node: Node) => string | null;
7
+ /** Visible inline text, including cached source atoms and native run tokens. */
8
+ declare const getInlineDisplayText: (paragraph: Node) => string;
9
+ //#endregion
10
+ export { getInlineDisplayText, getInlineDisplayToken, isNoteReferenceText };
@@ -0,0 +1,29 @@
1
+ import { decodeOoxmlSymbolCharacter } from "../utils/ooxmlSymbol.js";
2
+ import { expectFieldAttrs, expectPreservedXmlAttrs, expectSymbolAttrs } from "./attrs/index.js";
3
+ //#region src/prosemirror/inlineDisplayText.ts
4
+ /** Native note-reference labels are navigation anchors, outside searchable text. */
5
+ const isNoteReferenceText = (node) => node.isText && node.marks.some((mark) => mark.type.name === "footnoteRef");
6
+ /** A leaf's logical text; null means a transparent container to descend into. */
7
+ const getInlineDisplayToken = (node) => {
8
+ if (isNoteReferenceText(node)) return "";
9
+ if (node.type.name === "field") return expectFieldAttrs(node).displayText;
10
+ if (node.type.name === "preservedXml") return expectPreservedXmlAttrs(node).text;
11
+ if (node.isText) return node.text ?? "";
12
+ if (node.type.name === "tab") return " ";
13
+ if (node.type.name === "hardBreak") return "\n";
14
+ if (node.type.name === "symbol") return decodeOoxmlSymbolCharacter(expectSymbolAttrs(node).char) ?? "";
15
+ return null;
16
+ };
17
+ /** Visible inline text, including cached source atoms and native run tokens. */
18
+ const getInlineDisplayText = (paragraph) => {
19
+ let text = "";
20
+ paragraph.descendants((node) => {
21
+ const token = getInlineDisplayToken(node);
22
+ if (token === null) return true;
23
+ text += token;
24
+ return false;
25
+ });
26
+ return text;
27
+ };
28
+ //#endregion
29
+ export { getInlineDisplayText, getInlineDisplayToken, isNoteReferenceText };
@@ -1,3 +1,4 @@
1
+ import { document_d_exports } from "../types/document.js";
1
2
  import { DisplayMode } from "../managers/EditorModeManager.js";
2
3
  import { RemovedSectionReference } from "../internal/sectionEndpointResolution.js";
3
4
  import { indexedPositionMap } from "../internal/indexedPositionMap.js";
@@ -93,5 +94,13 @@ declare const projectMarkupView: (state: EditorState, view: DisplayMode) => Mark
93
94
  * of text it cannot enter.
94
95
  */
95
96
  declare const visibleCaretPosition: (projection: MarkupViewProjection, position: number) => number;
97
+ type ProjectStoryMarkupViewOptions = {
98
+ doc: Node;
99
+ markupView: DisplayMode;
100
+ styles?: document_d_exports.StyleDefinitions | null | undefined;
101
+ numbering?: document_d_exports.NumberingDefinitions | undefined;
102
+ };
103
+ /** Detached stories use the same state-aware projection owner as the body. */
104
+ declare const projectStoryMarkupView: ({ doc, markupView, styles, numbering }: ProjectStoryMarkupViewOptions) => MarkupViewProjection;
96
105
  //#endregion
97
- export { MarkupViewProjection, markupViewResolutionMode, projectMarkupView, resolveStateStory, visibleCaretPosition };
106
+ export { MarkupViewProjection, markupViewResolutionMode, projectMarkupView, projectStoryMarkupView, resolveStateStory, visibleCaretPosition };
@@ -1,9 +1,10 @@
1
1
  import { indexedPositionMap } from "../internal/indexedPositionMap.js";
2
2
  import { finalRevisionParagraphRanges } from "../internal/revisionResolutionTracking.js";
3
3
  import { resolveWholeStory } from "../internal/wholeStoryRevisionResolution.js";
4
- import { getDocumentNumbering } from "./plugins/documentNumbering.js";
4
+ import { createDocumentNumberingPlugin, getDocumentNumbering } from "./plugins/documentNumbering.js";
5
5
  import { getDocumentStyleResolver } from "./plugins/documentStyleState.js";
6
- import { Selection } from "prosemirror-state";
6
+ import { createDocumentStyleContextPlugin } from "./plugins/documentStyles.js";
7
+ import { EditorState, Selection } from "prosemirror-state";
7
8
  //#region src/prosemirror/markupViewProjection.ts
8
9
  /** Resolve every revision in the state's story with the state's own styles and numbering. */
9
10
  const resolveStateStory = (state, mode) => resolveWholeStory({
@@ -84,5 +85,22 @@ const visibleCaretPosition = (projection, position) => {
84
85
  const near = Selection.near(projection.doc.resolve(bounded), -1);
85
86
  return projection.toEditor(near.head, -1);
86
87
  };
88
+ const storyProjectionStates = /* @__PURE__ */ new WeakMap();
89
+ /** Detached stories use the same state-aware projection owner as the body. */
90
+ const projectStoryMarkupView = ({ doc, markupView, styles, numbering }) => {
91
+ if (markupView === "all-markup") return AUTHORED;
92
+ const cached = storyProjectionStates.get(doc);
93
+ if (cached && cached.styles === styles && cached.numbering === numbering) return projectMarkupView(cached.state, markupView);
94
+ const state = EditorState.create({
95
+ doc,
96
+ plugins: [createDocumentStyleContextPlugin(styles), createDocumentNumberingPlugin(numbering)]
97
+ });
98
+ storyProjectionStates.set(doc, {
99
+ styles,
100
+ numbering,
101
+ state
102
+ });
103
+ return projectMarkupView(state, markupView);
104
+ };
87
105
  //#endregion
88
- export { markupViewResolutionMode, projectMarkupView, resolveStateStory, visibleCaretPosition };
106
+ export { markupViewResolutionMode, projectMarkupView, projectStoryMarkupView, resolveStateStory, visibleCaretPosition };
@@ -1,3 +1,4 @@
1
+ import { sliceText } from "../../utils/sliceText.js";
1
2
  import { expectParagraphAttrs } from "../attrs/index.js";
2
3
  import { effectiveParagraphNumberingReference, paragraphNumberingSourcesFromBoundary } from "../numberingAttr.js";
3
4
  import { createPluginKeyOwner } from "../pluginKeyOwner.js";
@@ -30,7 +31,11 @@ const MAX_REPORTED_STEPS_LENGTH = 4e3;
30
31
  const describeSteps = (transactions) => {
31
32
  const steps = transactions.flatMap((transaction) => transaction.steps.map((step) => step.toJSON()));
32
33
  const json = JSON.stringify(steps);
33
- return json.length > MAX_REPORTED_STEPS_LENGTH ? `${json.slice(0, MAX_REPORTED_STEPS_LENGTH)}… (${json.length} characters)` : json;
34
+ return json.length > MAX_REPORTED_STEPS_LENGTH ? `${sliceText({
35
+ text: json,
36
+ units: "codeUnits",
37
+ end: MAX_REPORTED_STEPS_LENGTH
38
+ })}… (${json.length} characters)` : json;
34
39
  };
35
40
  var TransactionInvariantError = class extends Error {
36
41
  issues;
@@ -19,6 +19,20 @@ declare const NON_INCLUSIVE_MARK_DISPOSITION: {
19
19
  };
20
20
  /** The mark names in the schema that a replacement must not silently drop. */
21
21
  declare const nonInclusiveMarkNames: (schema: Schema) => string[];
22
+ /** One typed point for a comment whose aligned coverage has no content left. */
23
+ declare const commentPoint: (schema: Schema, id: number) => Node;
24
+ type ResolvedCommentPointsOptions = {
25
+ doc: Node;
26
+ removes: (node: Node, position: number) => boolean;
27
+ retiresStructure: (node: Node, position: number) => boolean;
28
+ };
29
+ /** The same collector serves standalone commands and the existing bulk walk. */
30
+ declare const createResolvedCommentPointCollector: ({ removes, retiresStructure }: Omit<ResolvedCommentPointsOptions, "doc">) => {
31
+ visit: (node: Node, position: number, previous?: Node) => boolean;
32
+ finish: () => Map<number, number>;
33
+ };
34
+ /** Point fallback excludes comments belonging to retired blocks and table cells. */
35
+ declare const resolvedCommentPoints: ({ doc, removes, retiresStructure }: ResolvedCommentPointsOptions) => Map<number, number>;
22
36
  type ReplacedAnnotations = {
23
37
  /** Marks the replacement must carry, in document order. */
24
38
  carried: readonly Mark[];
@@ -88,4 +102,4 @@ declare const annotatedReplacement: ({ annotations: { carried, leading, trailing
88
102
  */
89
103
  declare const inheritedReplacementMarks: (doc: Node, from: number, to: number) => readonly Mark[];
90
104
  //#endregion
91
- export { NON_INCLUSIVE_MARK_DISPOSITION, ReplacedAnnotations, addCarriedMarks, annotatedReplacement, commentsAcrossBlockBoundary, commentsForTrackedInsertion, hasReplacedAnnotations, inheritedReplacementMarks, nonInclusiveMarkNames, placeTrackedInsertion, surveyReplacedAnnotations };
105
+ export { NON_INCLUSIVE_MARK_DISPOSITION, ReplacedAnnotations, addCarriedMarks, annotatedReplacement, commentPoint, commentsAcrossBlockBoundary, commentsForTrackedInsertion, createResolvedCommentPointCollector, hasReplacedAnnotations, inheritedReplacementMarks, nonInclusiveMarkNames, placeTrackedInsertion, resolvedCommentPoints, surveyReplacedAnnotations };
@@ -1,9 +1,11 @@
1
+ import { expectParagraphAttrs, expectTableCellAttrs, expectTableRowAttrs } from "./attrs/index.js";
1
2
  import { BOOKMARK_BOUNDARY_NODE_NAME } from "./extensions/nodes/BookmarkBoundaryExtension.js";
2
3
  import { COMMENT_REFERENCE_NODE_NAME } from "./extensions/nodes/CommentReferenceExtension.js";
3
4
  import { MOVE_RANGE_BOUNDARY_NODE_NAME } from "./extensions/nodes/MoveRangeBoundaryExtension.js";
4
5
  import { RANGE_ANCHOR_NODE_NAME } from "./extensions/nodes/RangeAnchorExtension.js";
5
6
  import { TEXT_BOX_ANCHOR_NODE_NAME } from "./extensions/nodes/TextBoxAnchorExtension.js";
6
7
  import { readMoveRangeBoundaryAttrs } from "./moveRangeBoundaryAttrs.js";
8
+ import { readRangeAnchorAttrs } from "./rangeAnchorAttrs.js";
7
9
  import { isZeroWidthAnchor } from "./zeroWidthAnchors.js";
8
10
  import { Fragment, Mark } from "prosemirror-model";
9
11
  //#region src/prosemirror/replacedAnnotations.ts
@@ -21,15 +23,12 @@ import { Fragment, Mark } from "prosemirror-model";
21
23
  * hyperlink simply stops being a link. Zero-width anchors fare worse still: a
22
24
  * plain replace deletes them outright.
23
25
  *
24
- * Word moves a range start that sits inside a replaced span to the beginning
25
- * of the replacement and lets the range keep covering the new text; it keeps
26
- * a link across a replacement of its display text. A range wholly inside the
27
- * span collapses onto the replacement, and disappears only when every
28
- * character it covered is gone. One rule per marker kind, and that is the
29
- * whole contract:
26
+ * Text replacement preserves a comment's aligned coverage. Unchanged text
27
+ * keeps its ranges; inserted text takes the comments of the replaced aligned
28
+ * span. A range with no remaining content becomes a point at the replacement
29
+ * start. The applier decides alignment once for direct and tracked edits.
30
30
  *
31
- * - `comment` mark: the replacement carries every comment the span covered, so
32
- * a range that started inside it starts at the replacement instead.
31
+ * - `comment` mark: carried over its aligned replacement span.
33
32
  * - `hyperlink` mark: the replacement carries the first link the span held, so
34
33
  * replacing a link's display text leaves the link on the new text. A span
35
34
  * crossing two links keeps the one it opens in, because the mark excludes
@@ -66,9 +65,8 @@ import { Fragment, Mark } from "prosemirror-model";
66
65
  * - every other mark: formatting, and left to boundary inheritance, which
67
66
  * already gives the replacement the formatting of the text it replaces.
68
67
  *
69
- * Deleting the whole of a comment's span leaves it no mark anywhere, so
70
- * `CommentReferenceExtension` drops the orphan reference and the save drops
71
- * the comment: the one case where a range is meant to disappear.
68
+ * Removing the owning block removes its comment. Replacing its last covered
69
+ * text within a surviving block retains a point rather than dropping it.
72
70
  */
73
71
  /**
74
72
  * What a replacement does with each mark the schema declares
@@ -90,6 +88,67 @@ const NON_INCLUSIVE_MARK_DISPOSITION = {
90
88
  /** The mark names in the schema that a replacement must not silently drop. */
91
89
  const nonInclusiveMarkNames = (schema) => Object.entries(schema.marks).filter(([, type]) => type.spec.inclusive === false).map(([name]) => name);
92
90
  const isCarried = (mark) => NON_INCLUSIVE_MARK_DISPOSITION[mark.type.name] === "carry";
91
+ /** One typed point for a comment whose aligned coverage has no content left. */
92
+ const commentPoint = (schema, id) => schema.node(RANGE_ANCHOR_NODE_NAME, {
93
+ start: {
94
+ type: "commentRangeStart",
95
+ id
96
+ },
97
+ end: {
98
+ type: "commentRangeEnd",
99
+ id
100
+ }
101
+ });
102
+ /** The same collector serves standalone commands and the existing bulk walk. */
103
+ const createResolvedCommentPointCollector = ({ removes, retiresStructure }) => {
104
+ const points = /* @__PURE__ */ new Map();
105
+ const retained = /* @__PURE__ */ new Set();
106
+ const visit = (node, position, previous) => {
107
+ if (node.type.name === "paragraph") {
108
+ const mark = expectParagraphAttrs(node).pPrMark;
109
+ const precedingMark = previous?.type.name === "paragraph" ? expectParagraphAttrs(previous).pPrMark : null;
110
+ if ((mark?.kind === "del" || mark?.kind === "moveFrom") && retiresStructure(node, position) || (precedingMark?.kind === "del" || precedingMark?.kind === "moveFrom") && previous !== void 0 && retiresStructure(previous, position - previous.nodeSize)) {
111
+ let survivingContent = false;
112
+ node.descendants((inline, offset) => {
113
+ if (inline.isInline && !isZeroWidthAnchor(inline) && !removes(inline, position + 1 + offset)) survivingContent = true;
114
+ return !survivingContent;
115
+ });
116
+ if (!survivingContent) return false;
117
+ }
118
+ }
119
+ if (node.type.name === "tableRow" && expectTableRowAttrs(node).trDel && retiresStructure(node, position)) return false;
120
+ if ((node.type.name === "tableCell" || node.type.name === "tableHeader") && expectTableCellAttrs(node).cellMarker?.kind === "del" && retiresStructure(node, position)) return false;
121
+ if (node.type.name === "rangeAnchor") {
122
+ const attrs = readRangeAnchorAttrs(node);
123
+ if (attrs.ok && attrs.value.start.type === "commentRangeStart") retained.add(attrs.value.start.id);
124
+ }
125
+ if (!node.isInline || isZeroWidthAnchor(node)) return true;
126
+ for (const mark of node.marks) {
127
+ if (mark.type.name !== "comment") continue;
128
+ const id = mark.attrs["commentId"];
129
+ if (!removes(node, position)) retained.add(id);
130
+ else if (!points.has(id)) points.set(id, position);
131
+ }
132
+ return true;
133
+ };
134
+ const finish = () => {
135
+ for (const id of retained) points.delete(id);
136
+ return points;
137
+ };
138
+ return {
139
+ visit,
140
+ finish
141
+ };
142
+ };
143
+ /** Point fallback excludes comments belonging to retired blocks and table cells. */
144
+ const resolvedCommentPoints = ({ doc, removes, retiresStructure }) => {
145
+ const collector = createResolvedCommentPointCollector({
146
+ removes,
147
+ retiresStructure
148
+ });
149
+ doc.descendants((node, position, parent, index) => collector.visit(node, position, index > 0 ? parent?.maybeChild(index - 1) ?? void 0 : void 0));
150
+ return collector.finish();
151
+ };
93
152
  /** Anchors an edit must put back, and where relative to the replacement. */
94
153
  const ANCHOR_PLACEMENT = {
95
154
  [COMMENT_REFERENCE_NODE_NAME]: "after",
@@ -349,4 +408,4 @@ const inheritedReplacementMarks = (doc, from, to) => {
349
408
  return (to === from ? $from.marks() : $from.marksAcross(doc.resolve(to))) ?? Mark.none;
350
409
  };
351
410
  //#endregion
352
- export { NON_INCLUSIVE_MARK_DISPOSITION, addCarriedMarks, annotatedReplacement, commentsAcrossBlockBoundary, commentsForTrackedInsertion, hasReplacedAnnotations, inheritedReplacementMarks, nonInclusiveMarkNames, placeTrackedInsertion, surveyReplacedAnnotations };
411
+ export { NON_INCLUSIVE_MARK_DISPOSITION, addCarriedMarks, annotatedReplacement, commentPoint, commentsAcrossBlockBoundary, commentsForTrackedInsertion, createResolvedCommentPointCollector, hasReplacedAnnotations, inheritedReplacementMarks, nonInclusiveMarkNames, placeTrackedInsertion, resolvedCommentPoints, surveyReplacedAnnotations };
@@ -9,6 +9,8 @@ type ReadAuthoredRunFormattingOptions = {
9
9
  };
10
10
  /** Read authored run properties without confusing inherited visuals for direct formatting. */
11
11
  declare const readAuthoredRunFormatting: ({ context, marks, styleResolver }: ReadAuthoredRunFormattingOptions) => document_d_exports.TextFormatting;
12
+ /** Preserve the authored distinction between an absent and empty property set in revisions. */
13
+ declare const readAuthoredRunPropertySnapshot: (options: ReadAuthoredRunFormattingOptions) => document_d_exports.TextFormatting | undefined;
12
14
  type ReconcileRunFormattingMarksOptions = {
13
15
  authoredFormatting: document_d_exports.TextFormatting;
14
16
  context: ParagraphRunStyleContext;
@@ -22,4 +24,4 @@ type ReconcileRunFormattingMarksOptions = {
22
24
  */
23
25
  declare const reconcileRunFormattingMarks: ({ authoredFormatting, context, node, styleResolver }: ReconcileRunFormattingMarksOptions) => readonly Mark[];
24
26
  //#endregion
25
- export { readAuthoredRunFormatting, reconcileRunFormattingMarks };
27
+ export { readAuthoredRunFormatting, readAuthoredRunPropertySnapshot, reconcileRunFormattingMarks };