@stll/folio-core 0.60.0 → 0.61.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 (126) 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 +57 -63
  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/display-list/build/imagePrimitives.js +3 -6
  24. package/dist/docx/canonicalStoryRepack.js +3 -2
  25. package/dist/docx/commentTextParagraph.d.ts +6 -0
  26. package/dist/docx/commentTextParagraph.js +15 -0
  27. package/dist/docx/conformance.js +6 -1
  28. package/dist/docx/diagramPreview.js +2 -1
  29. package/dist/docx/documentParser.d.ts +13 -1
  30. package/dist/docx/documentParser.js +1 -1
  31. package/dist/docx/encryption/compoundFile.d.ts +0 -8
  32. package/dist/docx/encryption/compoundFile.js +2 -1
  33. package/dist/docx/encryption/cryptoBytes.d.ts +2 -2
  34. package/dist/docx/encryption/cryptoBytes.js +3 -18
  35. package/dist/docx/encryption/encryptionInfo.d.ts +0 -5
  36. package/dist/docx/encryption/encryptionInfo.js +2 -1
  37. package/dist/docx/ensureParaIds.js +2 -1
  38. package/dist/docx/groupDrawingParser.js +6 -1
  39. package/dist/docx/listModelNormalization.d.ts +10 -0
  40. package/dist/docx/listModelNormalization.js +166 -0
  41. package/dist/docx/metadataPrivacy.js +3 -2
  42. package/dist/docx/metafileRaster.js +2 -1
  43. package/dist/docx/packageParts.js +6 -5
  44. package/dist/docx/paragraphParser.js +6 -4
  45. package/dist/docx/parser.js +5 -4
  46. package/dist/docx/preservedRunContent.js +11 -2
  47. package/dist/docx/removeHeaderFooterParts.js +8 -7
  48. package/dist/docx/replyToComment.js +3 -13
  49. package/dist/docx/rezip.js +88 -89
  50. package/dist/docx/runParser.js +12 -6
  51. package/dist/docx/runPropertyPresence.d.ts +11 -0
  52. package/dist/docx/runPropertyPresence.js +14 -0
  53. package/dist/docx/selectiveSave.js +15 -14
  54. package/dist/docx/serializer/runSerializer.js +3 -2
  55. package/dist/docx/serializer/tableSerializer.js +2 -1
  56. package/dist/docx/server/applyDocxXmlPatchProposal.js +2 -2
  57. package/dist/docx/server/boundedArchive.js +2 -1
  58. package/dist/docx/server/evaluateDocxXmlPatchProposal.js +2 -1
  59. package/dist/docx/server/inspectDocxPackage.js +5 -1
  60. package/dist/docx/server/validateDocxConformance.js +2 -1
  61. package/dist/docx/tableParser.js +7 -4
  62. package/dist/docx/tableRowPropertyPresence.d.ts +9 -0
  63. package/dist/docx/tableRowPropertyPresence.js +15 -0
  64. package/dist/docx/unzip.js +2 -1
  65. package/dist/docx/xmlEncoding.js +6 -5
  66. package/dist/fonts/fontsourceFaces.js +2 -1
  67. package/dist/fonts/sfnt/parse.js +6 -9
  68. package/dist/fonts/sfnt/subset.js +2 -1
  69. package/dist/fonts/sfnt/tables.js +2 -3
  70. package/dist/fonts/sfnt/woff.js +2 -1
  71. package/dist/index.d.ts +3 -1
  72. package/dist/internal/revisionResolutionInline.d.ts +2 -1
  73. package/dist/internal/revisionResolutionInline.js +22 -1
  74. package/dist/internal/wholeStoryRevisionResolution.js +24 -7
  75. package/dist/layout-bridge/convert/flowConversionShared.d.ts +2 -0
  76. package/dist/layout-bridge/convert/footnoteLayout.d.ts +4 -0
  77. package/dist/layout-bridge/convert/footnoteLayout.js +13 -3
  78. package/dist/layout-bridge/convert/headerFooterLayout.d.ts +5 -0
  79. package/dist/layout-bridge/convert/headerFooterLayout.js +12 -15
  80. package/dist/layout-bridge/convert/paragraphRuns.js +2 -1
  81. package/dist/layout-engine/measure/crossRunWords.js +12 -2
  82. package/dist/managers/AutoSaveManager.js +2 -1
  83. package/dist/managers/EditorModeManager.d.ts +10 -1
  84. package/dist/managers/EditorModeManager.js +15 -1
  85. package/dist/managers/TableSelectionManager.d.ts +1 -1
  86. package/dist/managers/autoSaveCodec.js +2 -6
  87. package/dist/managers/reviewDisplayMode.d.ts +8 -0
  88. package/dist/managers/reviewDisplayMode.js +15 -0
  89. package/dist/paged-layout/pageText.d.ts +11 -2
  90. package/dist/paged-layout/pageText.js +8 -3
  91. package/dist/pdf/fonts.js +5 -14
  92. package/dist/pdf/images.js +3 -2
  93. package/dist/pdf/objects.d.ts +0 -11
  94. package/dist/pdf/objects.js +7 -9
  95. package/dist/pdf/writePdf.js +2 -1
  96. package/dist/prosemirror/commands/comments.js +51 -13
  97. package/dist/prosemirror/containerFinalParagraph.d.ts +8 -8
  98. package/dist/prosemirror/containerFinalParagraph.js +8 -8
  99. package/dist/prosemirror/conversion/fromProseDoc.js +9 -4
  100. package/dist/prosemirror/conversion/toProseDoc.js +4 -4
  101. package/dist/prosemirror/extensions/nodes/CommentReferenceExtension.js +16 -2
  102. package/dist/prosemirror/findReplaceSelection.d.ts +11 -1
  103. package/dist/prosemirror/findReplaceSelection.js +15 -1
  104. package/dist/prosemirror/markupViewProjection.d.ts +10 -1
  105. package/dist/prosemirror/markupViewProjection.js +21 -3
  106. package/dist/prosemirror/plugins/transactionInvariants.js +6 -1
  107. package/dist/prosemirror/replacedAnnotations.d.ts +15 -1
  108. package/dist/prosemirror/replacedAnnotations.js +71 -12
  109. package/dist/prosemirror/runFormattingReconciliation.d.ts +3 -1
  110. package/dist/prosemirror/runFormattingReconciliation.js +10 -2
  111. package/dist/prosemirror/runIdentityAcrossRevisions.d.ts +5 -7
  112. package/dist/prosemirror/runIdentityAcrossRevisions.js +66 -38
  113. package/dist/prosemirror/runPropertyChangeResolution.js +13 -1
  114. package/dist/utils/base64.d.ts +1 -31
  115. package/dist/utils/base64.js +19 -33
  116. package/dist/utils/clipboard.js +5 -4
  117. package/dist/utils/colorResolver.js +11 -2
  118. package/dist/utils/findReplace.d.ts +5 -0
  119. package/dist/utils/replaceText.js +21 -4
  120. package/dist/utils/sliceText.d.ts +2 -0
  121. package/dist/utils/sliceText.js +2 -0
  122. package/dist/utils/textEncoding.d.ts +2 -0
  123. package/dist/utils/textEncoding.js +2 -0
  124. package/dist/utils/tiffConverter.d.ts +0 -9
  125. package/dist/utils/tiffConverter.js +2 -4
  126. package/package.json +51 -25
@@ -245,10 +245,13 @@ const getTrackedChangeGroupsFromProjectedDoc = (doc, blockStarts) => {
245
245
  const key = previousKey !== void 0 && previousRange && transparentGap(previousRange.end, pos) ? previousKey : `${path}:${pos}`;
246
246
  lastInlineGroupByPath.set(path, key);
247
247
  const hyperlink = node.marks.find((candidate) => candidate.type.name === "hyperlink");
248
- const commentId = node.marks.find((candidate) => candidate.type.name === "comment")?.attrs["commentId"];
248
+ const commentIds = node.marks.flatMap((candidate) => {
249
+ const commentId = candidate.attrs["commentId"];
250
+ return candidate.type.name === "comment" && typeof commentId === "number" ? [commentId] : [];
251
+ });
249
252
  const boundary = {
250
253
  hyperlink: hyperlink ? String(hyperlink.attrs["href"] ?? "") : null,
251
- comment: typeof commentId === "number" ? commentId : null,
254
+ comments: commentIds.toSorted((left, right) => left - right).join(","),
252
255
  revisionParents: JSON.stringify(pathLayers.slice(0, layerIndex))
253
256
  };
254
257
  const range = inlineRanges.get(key);
@@ -275,23 +278,27 @@ const getTrackedChangeGroupsFromProjectedDoc = (doc, blockStarts) => {
275
278
  }
276
279
  });
277
280
  const groups = [];
278
- let previousKey = null;
281
+ let previousInline = null;
279
282
  for (const [key, change] of grouped) {
280
283
  const range = inlineRanges.get(key);
281
- const previous = groups.at(-1);
282
- const previousRange = previousKey === null ? void 0 : inlineRanges.get(previousKey);
283
- if (previous && range && previousRange && previousRange.end <= range.start && transparentGap(previousRange.end, range.start) && previousRange.lastBoundary.revisionParents === range.firstBoundary.revisionParents && (previousRange.lastBoundary.hyperlink !== range.firstBoundary.hyperlink || previousRange.lastBoundary.comment !== range.firstBoundary.comment || previousRange.end !== range.start) && (change.type === "insertion" || change.type === "deletion") && previous.change.type === change.type && previous.change.author === change.author && previous.change.date === change.date && previous.change.blockId === change.blockId) {
284
+ const previous = previousInline?.group;
285
+ const previousRange = previousInline === null ? void 0 : inlineRanges.get(previousInline.key);
286
+ if (previous && range && previousRange && previousRange.end <= range.start && transparentGap(previousRange.end, range.start) && previousRange.lastBoundary.revisionParents === range.firstBoundary.revisionParents && (previousRange.lastBoundary.hyperlink !== range.firstBoundary.hyperlink || previousRange.lastBoundary.comments !== range.firstBoundary.comments || previousRange.end !== range.start) && (change.type === "insertion" || change.type === "deletion") && previous.change.type === change.type && previous.change.author === change.author && previous.change.date === change.date && previous.change.blockId === change.blockId) {
284
287
  previous.change.text += change.text;
285
288
  previous.ids.push(change.id);
286
289
  previousRange.end = range.end;
287
290
  previousRange.lastBoundary = range.lastBoundary;
288
291
  continue;
289
292
  }
290
- groups.push({
293
+ const group = {
291
294
  change: { ...change },
292
295
  ids: [change.id]
293
- });
294
- previousKey = key;
296
+ };
297
+ groups.push(group);
298
+ if (range) previousInline = {
299
+ key,
300
+ group
301
+ };
295
302
  }
296
303
  return groups;
297
304
  };
@@ -1,16 +1,47 @@
1
- import { expectTrackedChangeMarkAttrs } from "../prosemirror/attrs/index.js";
1
+ import { expectRunPropertyChangeMarkAttrs, expectTrackedChangeMarkAttrs } from "../prosemirror/attrs/index.js";
2
+ import { applyMarksToRunFormattingRepresentation, expandRunFormattingCarrier } from "../prosemirror/runFormattingInlineCarriers.js";
3
+ import { panic } from "better-result";
2
4
  //#region src/ai-edits/revisionStretches.ts
3
5
  const INLINE_REVISION_MARKS = /* @__PURE__ */ new Set(["insertion", "deletion"]);
4
6
  const separateRevisionStretches = ({ tr, revisionSeed }) => {
5
7
  let nextRevisionId = revisionSeed;
6
8
  const minted = [];
7
9
  const retargets = [];
10
+ const formattingRetargets = [];
11
+ const formattingIds = /* @__PURE__ */ new Set();
12
+ const formattingRepresentations = /* @__PURE__ */ new Set();
8
13
  let stretches = /* @__PURE__ */ new Map();
9
14
  tr.doc.descendants((node, pos) => {
10
15
  if (node.isTextblock) {
11
16
  stretches = /* @__PURE__ */ new Map();
12
17
  return true;
13
18
  }
19
+ const carrier = node.marks.some(({ type }) => type.name === "runPropertyChange") ? expandRunFormattingCarrier(node, pos) : null;
20
+ for (const representation of carrier?.representations ?? []) {
21
+ if (carrier?.disposition === "structured-field" && representation.role === "owner") continue;
22
+ const mark = representation.node.marks.find(({ type }) => type.name === "runPropertyChange");
23
+ if (!mark) continue;
24
+ if (formattingRepresentations.has(representation.position)) continue;
25
+ formattingRepresentations.add(representation.position);
26
+ const ids = /* @__PURE__ */ new Map();
27
+ for (const { info } of expectRunPropertyChangeMarkAttrs(mark).changes) {
28
+ if (!formattingIds.has(info.id)) {
29
+ formattingIds.add(info.id);
30
+ continue;
31
+ }
32
+ const fresh = nextRevisionId++;
33
+ minted.push({
34
+ revisionId: fresh,
35
+ from: info.id
36
+ });
37
+ ids.set(info.id, fresh);
38
+ }
39
+ if (ids.size > 0) formattingRetargets.push({
40
+ representation,
41
+ mark,
42
+ ids
43
+ });
44
+ }
14
45
  if (!node.isInline || !(node.isLeaf || node.isText)) return true;
15
46
  const layersOf = (mark) => {
16
47
  const attrs = expectTrackedChangeMarkAttrs(mark);
@@ -71,6 +102,26 @@ const separateRevisionStretches = ({ tr, revisionSeed }) => {
71
102
  ...ancestors && { _docxRevisionAncestors: ancestors.map((ancestor) => Object.assign(structuredClone(ancestor), { revisionId: ids.get(ancestor.revisionId) ?? ancestor.revisionId })) }
72
103
  }));
73
104
  }
105
+ for (const { representation, mark, ids } of formattingRetargets) {
106
+ const live = tr.doc.nodeAt(representation.position);
107
+ if (!live) panic("A formatting revision carrier vanished during normalization");
108
+ const changes = expectRunPropertyChangeMarkAttrs(mark).changes.map((change) => Object.assign({}, change, { info: Object.assign({}, change.info, { id: ids.get(change.info.id) ?? change.info.id }) }));
109
+ const replacement = mark.type.create({
110
+ ...mark.attrs,
111
+ changes
112
+ });
113
+ applyMarksToRunFormattingRepresentation({
114
+ tr,
115
+ representation: {
116
+ node: live,
117
+ position: representation.position,
118
+ role: representation.role,
119
+ from: representation.position,
120
+ to: representation.position + representation.node.nodeSize
121
+ },
122
+ marks: replacement.addToSet(live.marks)
123
+ });
124
+ }
74
125
  return {
75
126
  nextRevisionId,
76
127
  minted
@@ -1,6 +1,7 @@
1
1
  import { addTrackedDeletionMark } from "../prosemirror/addTrackedDeletionMark.js";
2
2
  import { markStructuralChange } from "../prosemirror/extensions/features/ParagraphChangeTrackerExtension.js";
3
3
  import { markRowsEmptiedInBatch, reconcileTableGridAfterColumnInsertion, reconcileTableGridAfterColumnRemoval, removeRowsWithoutCells, removeTableRow } from "../prosemirror/tableGridMutation.js";
4
+ import { sliceText } from "../utils/sliceText.js";
4
5
  import { stripBlockIdentityAttrs } from "./block-identity.js";
5
6
  import { findEnclosingTableCell, findEnclosingTableRow } from "./table-targets.js";
6
7
  import { tableRowFromTemplate } from "./table-template.js";
@@ -16,7 +17,11 @@ const cellRevision = (revision, index) => ({
16
17
  revisionId: revision.revisionId + index
17
18
  });
18
19
  const UNPLACED_TEXT_PREVIEW_LENGTH = 40;
19
- const previewCellText = (text) => JSON.stringify(text.length > UNPLACED_TEXT_PREVIEW_LENGTH ? `${text.slice(0, UNPLACED_TEXT_PREVIEW_LENGTH)}…` : text);
20
+ const previewCellText = (text) => JSON.stringify(text.length > UNPLACED_TEXT_PREVIEW_LENGTH ? `${sliceText({
21
+ text,
22
+ units: "codeUnits",
23
+ end: UNPLACED_TEXT_PREVIEW_LENGTH
24
+ })}…` : text);
20
25
  /**
21
26
  * Name the `cellTexts` a new row or column has no cell for, or `null` when
22
27
  * every one of them has a place.
@@ -1,3 +1,4 @@
1
+ import { decodeText, encodeUtf8 } from "../utils/textEncoding.js";
1
2
  import { writeZipPart } from "@stll/docx-core/zip";
2
3
  import JSZip from "jszip";
3
4
  //#region src/compare/reproducible-package.ts
@@ -22,7 +23,7 @@ const withFixedPackageDates = async (buffer, date) => {
22
23
  if (coreProps) writeZipPart({
23
24
  zip,
24
25
  path: CORE_PROPERTIES_PATH,
25
- data: withFixedModifiedDate(await coreProps.async("text"), date),
26
+ data: encodeUtf8(withFixedModifiedDate(await coreProps.async("uint8array").then((bytes) => decodeText(bytes, { ignoreBOM: true })), date)),
26
27
  options: {
27
28
  compression: "DEFLATE",
28
29
  compressionOptions: { level: DOCX_COMPRESSION_LEVEL }
@@ -307,6 +307,7 @@ const revisedFinalParagraphMarks = (packageModel, { since = 0 } = {}) => {
307
307
  if (!isRecord(value) || value instanceof Date || ArrayBuffer.isView(value)) return;
308
308
  const inADeletedRow = insideADeletedRow || isADeletedTableRow(value);
309
309
  for (const [key, item] of Object.entries(value)) {
310
+ if (key === "sections" && Array.isArray(value["content"])) continue;
310
311
  trail.push(key);
311
312
  visit(item, inADeletedRow);
312
313
  trail.pop();
@@ -39,7 +39,9 @@ import { toMarkdown, toMarkdownResult } from "../markdown/index.js";
39
39
  import { EmbeddedFont, EmbeddedFontParts, buildEmbeddedFontFamilyMap, extractEmbeddedFonts, getEmbeddedFontFaces, scopeEmbeddedFontFamily } from "../fonts/embeddedFonts.js";
40
40
  import { getGoogleFontsEnabled, setGoogleFontsEnabled } from "../utils/fontResolver.js";
41
41
  import { FOLIO_YJS_ATTR_SCHEMA_VERSION, FolioYjsAttrSchemaVersion, FolioYjsAttrSchemaVersionError } from "../prosemirror/yjsDocumentMetadata.js";
42
+ import { EditorReadOnlyReason } from "../managers/EditorModeManager.js";
43
+ import { ReviewDisplayMode } from "../managers/reviewDisplayMode.js";
42
44
  import { DOCX_CONFORMANCE_CLASSES } from "../index.js";
43
45
  type Document = document_d_exports.Document;
44
46
  type DocxConformanceClass = document_d_exports.DocxConformanceClass;
45
- export { type AIBarStatus, type AIChatMode, type AICitation, type AICitationRange, type AICitationSource, type AIGenerateInput, type AISuggestion, type AISuggestionApplyMode, type AISuggestionPreset, type AISuggestionSeverity, type AISuggestionStatus, type AcceptAutocompleteResult, type AnonymizationMatch, type AnonymizationTerm, type ApplyFolioDocumentOperationsOptions, type ApplyResult, type AutocompleteSuggestionPluginOptions, type AutocompleteSuggestionState, type AutocompleteSuggestionStatus, type AutocompleteTriggerCheck, type AutocompleteTriggerOptions, type AutocompleteTriggerSkipReason, type BlockRect, COMPARE_REVISION_FORMATS, COMPARE_UNSUPPORTED_REASONS, COMPARE_VERIFICATION_CAUSES, COMPARE_VERIFICATION_INVARIANTS, type CompareChange, type CompareChangeLocation, type CompareCompatibility, type CompareContentOptions, CompareDocxApplyError, type CompareDocxError, CompareDocxFinalParagraphMarkError, CompareDocxOperationLimitError, type CompareDocxOptions, CompareDocxParseError, CompareDocxRoundTripError, CompareDocxSerializeError, type CompareFolioRequirement, type CompareResult, type CompareRevisionFormat, type CompareUnsupportedPart, type CompareUnsupportedReason, type CompareVerification, type CompareVerificationCause, type CompareVerificationFailure, type CompareVerificationInvariant, type CreateEmptyDocumentOptions, DEFAULT_AI_SUGGESTION_PRESETS, DEFAULT_AUTOCOMPLETE_DEAD_ZONE_NODES, DEFAULT_OUTLINE_DEPTH, DOCUMENT_PRESET_VERSION, DOCUMENT_STYLE_SET_VERSION, DOCX_CONFORMANCE_CLASSES, DRAWING_SAFETY_CLASSES, type DeriveBlockIdInput, type DirectiveKind, type DirectiveRange, type Document, type DocumentPreset, type DocumentPropertiesOptions, type DocumentStyleCatalog, type DocumentStyleCatalogEntry, type DocumentStyleSet, type DocxCompatibility, type DocxCompatibilityContext, type DocxCompatibilityIssue, type DocxCompatibilityLocation, type DocxCompatibilityPart, type DocxConformanceClass, type DocxDrawingClassification, type DrawingSafetyClass, type EmbeddedFont, type EmbeddedFontParts, type ExtractDocumentStyleSetOptions, FOLIO_CONTENT_COMPARISON_LIMITS, FOLIO_DOCUMENT_OPERATION_BATCH_MODES, FOLIO_DOCUMENT_OPERATION_CONTRACT_VERSION, FOLIO_DOCUMENT_OPERATION_MODES, FOLIO_DOCUMENT_OPERATION_MODES_BY_TYPE, FOLIO_DOCUMENT_OPERATION_PRECONDITIONS, FOLIO_DOCUMENT_OPERATION_STORIES, FOLIO_DOCUMENT_OPERATION_TYPES, FOLIO_LINE_SPACING_RULE_VALUES, FOLIO_PARAGRAPH_ALIGNMENT_VALUES, FOLIO_YJS_ATTR_SCHEMA_VERSION, type FinalParagraphMarkRevision, type FolioAIBlock, type FolioAIBlockAnchor, type FolioAIBlockKind, type FolioAIBlockPreviewRun, type FolioAIBlockStructuralBoundary, type FolioAIBlockTableLocation, type FolioAIComment, type FolioAIDiagnosticBlock, type FolioAIEditAppliedOperation, type FolioAIEditApplyMode, type FolioAIEditApplyOutcome, type FolioAIEditApplyResult, type FolioAIEditNormalization, type FolioAIEditNormalizationCode, type FolioAIEditOperation, type FolioAIEditPrecondition, type FolioAIEditReviewMeta, type FolioAIEditSeverity, type FolioAIEditSkipReason, type FolioAIEditSkippedOperation, type FolioAIEditSnapshot, type FolioAIInlineBooleanProperty, type FolioAIInlineFormatting, type FolioAIInlineFormattingPatch, type FolioAIParagraphBlock, type FolioAIParagraphSpacing, type FolioAISignatureParty, type FolioBlockId, type FolioContentBlock, type FolioContentBlockProperty, type FolioContentComparison, type FolioContentComparisonError, type FolioContentComparisonEvent, type FolioContentComparisonLimit, FolioContentComparisonLimitError, type FolioContentContainerPathEntry, type FolioContentFormatRange, type FolioContentFormattingChange, type FolioContentIdStability, type FolioContentInlineBooleanProperty, type FolioContentInlineComparisonResult, type FolioContentInlineFormatting, type FolioContentInlineFormattingPatch, FolioContentInlinePresentationProjectionError, type FolioContentLineSpacingRule, type FolioContentParagraphAlignment, type FolioContentParagraphFormattingPatch, type FolioContentParagraphKind, type FolioContentParagraphSpacing, type FolioContentRun, type FolioContentSnapshot, type FolioContentStatedNumbering, type FolioContentStructuralChange, type FolioContentTableLocation, type FolioContentTextSegment, type FolioDocumentOperation, type FolioDocumentOperationAffectedTarget, type FolioDocumentOperationBatch, type FolioDocumentOperationCapabilities, type FolioDocumentOperationIssue, type FolioDocumentOperationMode, type FolioDocumentOperationPrecondition, type FolioDocumentOperationReceipt, type FolioDocumentOperationRecovery, type FolioDocumentOperationResult, type FolioDocumentOperationResultBase, type FolioDocumentOperationStatus, type FolioDocumentOperationStory, type FolioDocumentOperationType, type FolioDocumentOperationUndoFailureReason, type FolioDocumentOperationUndoHandle, type FolioDocumentOperationUndoResult, type FolioDocxCompatibilityHost, type FolioDocxCompatibilityProfile, type FolioRevisionStamp, type FolioUndefinedReferencePolicy, type FolioWordDiffOptions, type FolioYjsAttrSchemaVersion, FolioYjsAttrSchemaVersionError, type ImageMeta, type ImageRef, type InspectDocxCompatibilityOptions, InvalidCompareDocxOptionsError, InvalidFolioContentComparisonError, InvalidFolioDocumentOperationBatchError, MAX_COMPARE_OPERATIONS, type MarkdownOptions, type MarkdownResult, type OutlineDepth, type PositionalText, type ResolvedAnchor, STELLA_STYLE_SET_NAME, type TemplatePreviewHiddenRange, type TemplatePreviewSpan, type TemplatePreviewValue, type TemplatePreviewValues, type TemplateSlashMenuKeyAction, type TemplateSlashMenuState, UnsupportedFolioDocumentOperationVersionError, WORD_DIFF_GRANULARITIES, type WordDiffGranularity, type WordDiffNormalization, type WordDiffOptions, type WordDiffSegment, acceptAutocompleteSuggestion, acceptAutocompleteWord, anonymizationDecorationsKey, appendAutocompleteToken, applyFolioAIEditOperations, applyFolioDocumentOperations, applySuggestions, assertSupportedFolioDocumentOperationVersion, autocompleteSuggestionKey, autocompleteSuggestionPlugin, buildEmbeddedFontFamilyMap, buildPositionalText, clearAutocompleteSuggestion, clearTemplateSlashMenu, compareContent, compareDocx, consumeTemplateSlashQuery, createAICitationDecorationsPlugin, createDocx, createEmptyDocument, createFolioAIEditSnapshot, createStellaStyleDocumentPreset, createStellaStyleSet, currentFolioBlockId, deriveBlockId, diffWordSegments, extractDocumentStyleSet, extractDocumentStyleSetFromDocx, extractEmbeddedFonts, filterHeadingsByDepth, finishAutocompleteSuggestion, fromMarkdown, getAnonymizationMatches, getAutocompleteSuggestion, getEmbeddedFontFaces, getFolioCaretViewportRect, getFolioDocumentOperationCapabilities, getFolioDocumentOperationIssues, getFolioDocumentOperationReceipts, getFolioParaIdFromBlockId, getFolioSelectionViewportRect, getGoogleFontsEnabled, getTemplateDirectives, getTemplateSlashMenu, hashFolioAIBlockText, inspectDocumentStyles, inspectDocumentStylesFromDocx, inspectDocxCompatibility, isFolioAIContentBlock, isFolioBlockId, isFolioDocumentOperationModeSupported, isSequentialFolioBlockId, isSuggestionStale, isSupportedFolioDocumentOperationVersion, mergeDocumentContent, normalizeFolioAIBlockText, parseFolioDocumentOperationBatch, resetTemplateSlashQuery, resolveSuggestionAnchor, scanDirectives, scopeEmbeddedFontFamily, scrollFolioPositionIntoView, setAICitationsMeta, setAISuggestionsMeta, setActiveCitationMeta, setAnonymizationTermsMeta, setFocusedSuggestionMeta, setGoogleFontsEnabled, setTemplatePreviewValues, shouldTriggerAutocomplete, startAutocompleteSuggestion, templateSlashMenuKey, toMarkdown, toMarkdownResult };
47
+ export { type AIBarStatus, type AIChatMode, type AICitation, type AICitationRange, type AICitationSource, type AIGenerateInput, type AISuggestion, type AISuggestionApplyMode, type AISuggestionPreset, type AISuggestionSeverity, type AISuggestionStatus, type AcceptAutocompleteResult, type AnonymizationMatch, type AnonymizationTerm, type ApplyFolioDocumentOperationsOptions, type ApplyResult, type AutocompleteSuggestionPluginOptions, type AutocompleteSuggestionState, type AutocompleteSuggestionStatus, type AutocompleteTriggerCheck, type AutocompleteTriggerOptions, type AutocompleteTriggerSkipReason, type BlockRect, COMPARE_REVISION_FORMATS, COMPARE_UNSUPPORTED_REASONS, COMPARE_VERIFICATION_CAUSES, COMPARE_VERIFICATION_INVARIANTS, type CompareChange, type CompareChangeLocation, type CompareCompatibility, type CompareContentOptions, CompareDocxApplyError, type CompareDocxError, CompareDocxFinalParagraphMarkError, CompareDocxOperationLimitError, type CompareDocxOptions, CompareDocxParseError, CompareDocxRoundTripError, CompareDocxSerializeError, type CompareFolioRequirement, type CompareResult, type CompareRevisionFormat, type CompareUnsupportedPart, type CompareUnsupportedReason, type CompareVerification, type CompareVerificationCause, type CompareVerificationFailure, type CompareVerificationInvariant, type CreateEmptyDocumentOptions, DEFAULT_AI_SUGGESTION_PRESETS, DEFAULT_AUTOCOMPLETE_DEAD_ZONE_NODES, DEFAULT_OUTLINE_DEPTH, DOCUMENT_PRESET_VERSION, DOCUMENT_STYLE_SET_VERSION, DOCX_CONFORMANCE_CLASSES, DRAWING_SAFETY_CLASSES, type DeriveBlockIdInput, type DirectiveKind, type DirectiveRange, type Document, type DocumentPreset, type DocumentPropertiesOptions, type DocumentStyleCatalog, type DocumentStyleCatalogEntry, type DocumentStyleSet, type DocxCompatibility, type DocxCompatibilityContext, type DocxCompatibilityIssue, type DocxCompatibilityLocation, type DocxCompatibilityPart, type DocxConformanceClass, type DocxDrawingClassification, type DrawingSafetyClass, type EditorReadOnlyReason, type EmbeddedFont, type EmbeddedFontParts, type ExtractDocumentStyleSetOptions, FOLIO_CONTENT_COMPARISON_LIMITS, FOLIO_DOCUMENT_OPERATION_BATCH_MODES, FOLIO_DOCUMENT_OPERATION_CONTRACT_VERSION, FOLIO_DOCUMENT_OPERATION_MODES, FOLIO_DOCUMENT_OPERATION_MODES_BY_TYPE, FOLIO_DOCUMENT_OPERATION_PRECONDITIONS, FOLIO_DOCUMENT_OPERATION_STORIES, FOLIO_DOCUMENT_OPERATION_TYPES, FOLIO_LINE_SPACING_RULE_VALUES, FOLIO_PARAGRAPH_ALIGNMENT_VALUES, FOLIO_YJS_ATTR_SCHEMA_VERSION, type FinalParagraphMarkRevision, type FolioAIBlock, type FolioAIBlockAnchor, type FolioAIBlockKind, type FolioAIBlockPreviewRun, type FolioAIBlockStructuralBoundary, type FolioAIBlockTableLocation, type FolioAIComment, type FolioAIDiagnosticBlock, type FolioAIEditAppliedOperation, type FolioAIEditApplyMode, type FolioAIEditApplyOutcome, type FolioAIEditApplyResult, type FolioAIEditNormalization, type FolioAIEditNormalizationCode, type FolioAIEditOperation, type FolioAIEditPrecondition, type FolioAIEditReviewMeta, type FolioAIEditSeverity, type FolioAIEditSkipReason, type FolioAIEditSkippedOperation, type FolioAIEditSnapshot, type FolioAIInlineBooleanProperty, type FolioAIInlineFormatting, type FolioAIInlineFormattingPatch, type FolioAIParagraphBlock, type FolioAIParagraphSpacing, type FolioAISignatureParty, type FolioBlockId, type FolioContentBlock, type FolioContentBlockProperty, type FolioContentComparison, type FolioContentComparisonError, type FolioContentComparisonEvent, type FolioContentComparisonLimit, FolioContentComparisonLimitError, type FolioContentContainerPathEntry, type FolioContentFormatRange, type FolioContentFormattingChange, type FolioContentIdStability, type FolioContentInlineBooleanProperty, type FolioContentInlineComparisonResult, type FolioContentInlineFormatting, type FolioContentInlineFormattingPatch, FolioContentInlinePresentationProjectionError, type FolioContentLineSpacingRule, type FolioContentParagraphAlignment, type FolioContentParagraphFormattingPatch, type FolioContentParagraphKind, type FolioContentParagraphSpacing, type FolioContentRun, type FolioContentSnapshot, type FolioContentStatedNumbering, type FolioContentStructuralChange, type FolioContentTableLocation, type FolioContentTextSegment, type FolioDocumentOperation, type FolioDocumentOperationAffectedTarget, type FolioDocumentOperationBatch, type FolioDocumentOperationCapabilities, type FolioDocumentOperationIssue, type FolioDocumentOperationMode, type FolioDocumentOperationPrecondition, type FolioDocumentOperationReceipt, type FolioDocumentOperationRecovery, type FolioDocumentOperationResult, type FolioDocumentOperationResultBase, type FolioDocumentOperationStatus, type FolioDocumentOperationStory, type FolioDocumentOperationType, type FolioDocumentOperationUndoFailureReason, type FolioDocumentOperationUndoHandle, type FolioDocumentOperationUndoResult, type FolioDocxCompatibilityHost, type FolioDocxCompatibilityProfile, type FolioRevisionStamp, type FolioUndefinedReferencePolicy, type FolioWordDiffOptions, type FolioYjsAttrSchemaVersion, FolioYjsAttrSchemaVersionError, type ImageMeta, type ImageRef, type InspectDocxCompatibilityOptions, InvalidCompareDocxOptionsError, InvalidFolioContentComparisonError, InvalidFolioDocumentOperationBatchError, MAX_COMPARE_OPERATIONS, type MarkdownOptions, type MarkdownResult, type OutlineDepth, type PositionalText, type ResolvedAnchor, type ReviewDisplayMode, STELLA_STYLE_SET_NAME, type TemplatePreviewHiddenRange, type TemplatePreviewSpan, type TemplatePreviewValue, type TemplatePreviewValues, type TemplateSlashMenuKeyAction, type TemplateSlashMenuState, UnsupportedFolioDocumentOperationVersionError, WORD_DIFF_GRANULARITIES, type WordDiffGranularity, type WordDiffNormalization, type WordDiffOptions, type WordDiffSegment, acceptAutocompleteSuggestion, acceptAutocompleteWord, anonymizationDecorationsKey, appendAutocompleteToken, applyFolioAIEditOperations, applyFolioDocumentOperations, applySuggestions, assertSupportedFolioDocumentOperationVersion, autocompleteSuggestionKey, autocompleteSuggestionPlugin, buildEmbeddedFontFamilyMap, buildPositionalText, clearAutocompleteSuggestion, clearTemplateSlashMenu, compareContent, compareDocx, consumeTemplateSlashQuery, createAICitationDecorationsPlugin, createDocx, createEmptyDocument, createFolioAIEditSnapshot, createStellaStyleDocumentPreset, createStellaStyleSet, currentFolioBlockId, deriveBlockId, diffWordSegments, extractDocumentStyleSet, extractDocumentStyleSetFromDocx, extractEmbeddedFonts, filterHeadingsByDepth, finishAutocompleteSuggestion, fromMarkdown, getAnonymizationMatches, getAutocompleteSuggestion, getEmbeddedFontFaces, getFolioCaretViewportRect, getFolioDocumentOperationCapabilities, getFolioDocumentOperationIssues, getFolioDocumentOperationReceipts, getFolioParaIdFromBlockId, getFolioSelectionViewportRect, getGoogleFontsEnabled, getTemplateDirectives, getTemplateSlashMenu, hashFolioAIBlockText, inspectDocumentStyles, inspectDocumentStylesFromDocx, inspectDocxCompatibility, isFolioAIContentBlock, isFolioBlockId, isFolioDocumentOperationModeSupported, isSequentialFolioBlockId, isSuggestionStale, isSupportedFolioDocumentOperationVersion, mergeDocumentContent, normalizeFolioAIBlockText, parseFolioDocumentOperationBatch, resetTemplateSlashQuery, resolveSuggestionAnchor, scanDirectives, scopeEmbeddedFontFamily, scrollFolioPositionIntoView, setAICitationsMeta, setAISuggestionsMeta, setActiveCitationMeta, setAnonymizationTermsMeta, setFocusedSuggestionMeta, setGoogleFontsEnabled, setTemplatePreviewValues, shouldTriggerAutocomplete, startAutocompleteSuggestion, templateSlashMenuKey, toMarkdown, toMarkdownResult };
@@ -9,6 +9,7 @@ import { completeNumberingForDoc } from "../prosemirror/listInstanceReferences.j
9
9
  import { marksToTextFormatting } from "../prosemirror/runFormattingFromMarks.js";
10
10
  import { CANONICAL_GAP } from "../types/canonicalCapabilities.js";
11
11
  import { bytesToDataUrl } from "../utils/base64.js";
12
+ import { base64ToBytes } from "../utils/textEncoding.js";
12
13
  import { flattenClipboardStyleReferences, importClipboardStyles } from "./canonicalClipboardResources.js";
13
14
  import { CanonicalSessionError } from "./canonicalSession.js";
14
15
  import { Result } from "better-result";
@@ -407,7 +408,7 @@ const prepareCanonicalPaste = ({ session, state, slice, plain = false, moveTarge
407
408
  if (mimeType === void 0 || payload === void 0) return refuse("The clipboard image data is malformed.");
408
409
  if (payload.length > Math.ceil(MAX_CLIPBOARD_IMAGE_BYTES * 4 / 3)) return refuse("The clipboard image exceeds the import size limit.");
409
410
  const decoded = Result.try({
410
- try: () => Uint8Array.from(atob(payload), (char) => char.charCodeAt(0)),
411
+ try: () => base64ToBytes(payload),
411
412
  catch: () => new CanonicalSessionError({
412
413
  gap: CANONICAL_GAP.dispatch,
413
414
  message: "The clipboard image data is malformed.",
@@ -1,10 +1,8 @@
1
- import { document_d_exports } from "../types/document.js";
2
- import { DocumentOp } from "@stll/docx-core/ops";
3
1
  //#region src/controller/canonicalListRendering.d.ts
4
- type CanonicalListNormalization = {
5
- document: document_d_exports.Document;
6
- inverse: DocumentOp[];
2
+ /** Canonical edits share list model normalization with the headless projection. */
3
+ declare const normalizeCanonicalListRendering: (document: import("@stll/docx-core").Document) => {
4
+ document: import("@stll/docx-core").Document;
5
+ inverse: import("@stll/docx-core/ops").DocumentOp[];
7
6
  };
8
- declare const normalizeCanonicalListRendering: (document: document_d_exports.Document) => CanonicalListNormalization;
9
7
  //#endregion
10
8
  export { normalizeCanonicalListRendering };
@@ -1,160 +1,6 @@
1
- import { foldListNumberFields, planParagraphListNumberFold, unfoldedListNumberContent } from "../docx/foldedListNumberFields.js";
2
- import { computeListMarker } from "../docx/listMarkerComputation.js";
3
- import { listRenderingDefinitionsMatch } from "../docx/listRendering.js";
4
- import { computeListRendering, getCachedNumberingMap } from "../docx/numberingParser.js";
5
- import { assignParagraphIndentationProjection, cloneParagraphWithPropertySource, paragraphFormattingWithAuthoredIndentation, sameParagraphIndentationProjection } from "../docx/paragraphPropertySource.js";
6
- import { effectiveParagraphNumberingReference, paragraphNumberingSourcesFromBoundary } from "../prosemirror/numberingAttr.js";
7
- import { paragraphIndentationFromFormatting } from "../prosemirror/paragraphIndentation.js";
8
- import { listIndentationProvenancePatch } from "../prosemirror/styles/resolvedStyleAttrs.js";
9
- import { createStyleResolver } from "../prosemirror/styles/styleResolver.js";
10
- import { canonicalJson } from "../utils/canonicalJson.js";
11
- import { panic } from "better-result";
12
- import { DOCUMENT_OP_TYPES, EMPTY_PROPERTY_SETS, OP_STORIES, registerParagraphPropertySnapshot, withBodyContent } from "@stll/docx-core/ops";
1
+ import { normalizeListModelRendering } from "../docx/listModelNormalization.js";
13
2
  //#region src/controller/canonicalListRendering.ts
14
- /** Canonical edits retain authored fields; list rendering follows the current numbering. */
15
- const normalizeCanonicalListRendering = (document) => {
16
- const inverse = [];
17
- const styles = createStyleResolver(document.package.styles);
18
- const numbering = document.package.numbering === void 0 ? null : getCachedNumberingMap(document.package.numbering);
19
- const normalizeBody = (body, story) => {
20
- const counters = {
21
- numbering,
22
- listCounters: /* @__PURE__ */ new Map(),
23
- abstractCounters: /* @__PURE__ */ new Map(),
24
- restartedNumIds: /* @__PURE__ */ new Set(),
25
- previousList: {
26
- abstractNumId: null,
27
- fromStyle: false,
28
- numId: null
29
- }
30
- };
31
- const content = body.content.map((paragraph) => {
32
- if (paragraph.type !== "paragraph") return paragraph;
33
- registerParagraphPropertySnapshot({
34
- paragraph,
35
- snapshot: paragraphFormattingWithAuthoredIndentation(paragraph)
36
- });
37
- const formatting = paragraph.formatting;
38
- const next = cloneParagraphWithPropertySource(paragraph, {});
39
- if (formatting?.numPrFromStyle !== void 0 && (formatting.numPr?.kind === "reference" || formatting.numPr?.kind === "none")) {
40
- next.formatting = { ...formatting };
41
- delete next.formatting.numPrFromStyle;
42
- inverse.push({
43
- type: DOCUMENT_OP_TYPES.SET_PARAGRAPH_PROPS,
44
- story,
45
- blockId: paragraph.paraId ?? panic("Canonical numbering normalization requires paragraph identities."),
46
- patch: { numPrFromStyle: formatting.numPrFromStyle }
47
- });
48
- }
49
- const numPr = effectiveParagraphNumberingReference(paragraphNumberingSourcesFromBoundary({
50
- numPr: next.formatting?.numPr,
51
- numPrFromStyle: next.formatting?.numPrFromStyle
52
- }));
53
- if (paragraph.listRendering === void 0 && formatting?.numPrFromStyle === void 0 && (numPr?.kind !== "reference" || numbering === null)) {
54
- computeListMarker(paragraph, counters);
55
- return paragraph;
56
- }
57
- const definition = numPr?.kind === "reference" && numbering !== null ? computeListRendering(numPr, numbering) : null;
58
- const cached = paragraph.listRendering;
59
- const rendering = definition !== null && cached !== void 0 && listRenderingDefinitionsMatch(cached, definition) ? { ...cached } : definition;
60
- const authoredFormatting = paragraphFormattingWithAuthoredIndentation(next);
61
- assignParagraphIndentationProjection({
62
- paragraph: next,
63
- authored: authoredFormatting,
64
- inherited: paragraphIndentationFromFormatting(listIndentationProvenancePatch({
65
- direct: paragraphIndentationFromFormatting(authoredFormatting),
66
- styleFormatting: styles.resolveParagraphStyle(next.formatting?.styleId).paragraphFormatting,
67
- numberingSource: next.formatting?.numPr?.kind === "reference" || next.formatting?.numPrFromStyle === void 0 ? "paragraph" : "style",
68
- numPr: numPr?.kind === "reference" ? {
69
- numId: numPr.numId,
70
- ilvl: numPr.ilvl ?? 0
71
- } : void 0,
72
- numbering
73
- })._resolvedFormatting) ?? {}
74
- });
75
- if (canonicalJson(paragraphIndentationFromFormatting(next.formatting)) !== canonicalJson(paragraphIndentationFromFormatting(formatting))) inverse.unshift({
76
- type: DOCUMENT_OP_TYPES.SET_PARAGRAPH_PROPS,
77
- story,
78
- blockId: paragraph.paraId ?? panic("Canonical numbering normalization requires paragraph identities."),
79
- patch: {
80
- indentLeft: formatting?.indentLeft ?? null,
81
- indentRight: formatting?.indentRight ?? null,
82
- indentFirstLine: formatting?.indentFirstLine ?? null,
83
- hangingIndent: formatting?.hangingIndent ?? null
84
- },
85
- whenEmpty: formatting === void 0 ? EMPTY_PROPERTY_SETS.OMIT : EMPTY_PROPERTY_SETS.KEEP
86
- });
87
- if (rendering === null) delete next.listRendering;
88
- else {
89
- const fields = foldListNumberFields(paragraph.content.map(unfoldedListNumberContent), () => void 0).fieldCount;
90
- if (fields === 0) delete rendering.implicitChildLevelAdvances;
91
- else rendering.implicitChildLevelAdvances = fields;
92
- const template = rendering.markerTemplate ?? rendering.marker;
93
- const fold = planParagraphListNumberFold(paragraph.content, !rendering.isBullet && template !== "" && !template.includes(" "));
94
- if (fold.suffix === void 0) delete rendering.foldedMarkerSuffix;
95
- else rendering.foldedMarkerSuffix = fold.suffix;
96
- const child = numbering?.getLevel(rendering.numId, rendering.level + 1)?.pPr;
97
- if (fold.suffix !== void 0 && child?.hangingIndent === true && child.indentFirstLine !== void 0 && child.indentFirstLine < 0) rendering.markerSecondSlotOffsetTwips = -child.indentFirstLine;
98
- else delete rendering.markerSecondSlotOffsetTwips;
99
- rendering.marker = rendering.foldedMarkerSuffix !== void 0 && !template.includes(" ") ? `${template}\t${rendering.foldedMarkerSuffix}` : template;
100
- next.listRendering = rendering;
101
- }
102
- computeListMarker(next, counters);
103
- registerParagraphPropertySnapshot({
104
- paragraph: next,
105
- snapshot: authoredFormatting
106
- });
107
- if (sameParagraphIndentationProjection({
108
- left: next,
109
- right: paragraph
110
- }) && canonicalJson(next.formatting) === canonicalJson(formatting) && canonicalJson(next.listRendering) === canonicalJson(cached)) return paragraph;
111
- return next;
112
- });
113
- return content.every((paragraph, index) => paragraph === body.content[index]) ? body : {
114
- ...body,
115
- ...withBodyContent(body, content)
116
- };
117
- };
118
- const pkg = document.package;
119
- const body = normalizeBody(pkg.document, OP_STORIES.MAIN);
120
- const normalizeParts = (parts, kind) => {
121
- if (parts === void 0) return parts;
122
- const normalized = new Map([...parts].map(([id, part]) => [id, normalizeBody(part, {
123
- kind,
124
- rId: id
125
- })]));
126
- return [...normalized].every(([id, part]) => part === parts.get(id)) ? parts : normalized;
127
- };
128
- const normalizeNotes = (notes, kind) => {
129
- if (notes === void 0) return notes;
130
- const normalized = notes.map((note) => normalizeBody(note, {
131
- kind,
132
- id: note.id
133
- }));
134
- return normalized.every((note, index) => note === notes[index]) ? notes : normalized;
135
- };
136
- const headers = normalizeParts(pkg.headers, "header");
137
- const footers = normalizeParts(pkg.footers, "footer");
138
- const footnotes = normalizeNotes(pkg.footnotes, "footnote");
139
- const endnotes = normalizeNotes(pkg.endnotes, "endnote");
140
- if (body === pkg.document && headers === pkg.headers && footers === pkg.footers && footnotes === pkg.footnotes && endnotes === pkg.endnotes) return {
141
- document,
142
- inverse
143
- };
144
- return {
145
- inverse,
146
- document: {
147
- ...document,
148
- package: {
149
- ...pkg,
150
- document: body,
151
- ...headers === void 0 ? {} : { headers },
152
- ...footers === void 0 ? {} : { footers },
153
- ...footnotes === void 0 ? {} : { footnotes },
154
- ...endnotes === void 0 ? {} : { endnotes }
155
- }
156
- }
157
- };
158
- };
3
+ /** Canonical edits share list model normalization with the headless projection. */
4
+ const normalizeCanonicalListRendering = normalizeListModelRendering;
159
5
  //#endregion
160
6
  export { normalizeCanonicalListRendering };
@@ -18,6 +18,7 @@ type HeaderFooterEditorTransaction = {
18
18
  type HeaderFooterEditorManagerDeps = {
19
19
  createView?: ((mountNode: HTMLElement, props: DirectEditorProps) => EditorView) | undefined;
20
20
  getDocument: () => document_d_exports.Document | null;
21
+ getReadOnly?: (() => boolean) | undefined;
21
22
  getCanonicalApi?: (() => HiddenEditorApi | null) | undefined;
22
23
  getExperimentalSession?: (() => "canonical" | undefined) | undefined;
23
24
  onSessionRefusal?: ((reason: string, gap: CanonicalGap, error?: Error) => void) | undefined;
@@ -91,6 +91,15 @@ const createHeaderFooterEditorManager = (deps) => {
91
91
  const sync = () => {
92
92
  const host = deps.getHost();
93
93
  if (!host) return;
94
+ const getReadOnly = deps.getReadOnly;
95
+ if (getReadOnly) {
96
+ const readOnly = getReadOnly();
97
+ for (const part of mounted.values()) {
98
+ const expectedEditable = !readOnly;
99
+ if (part.view.editable === expectedEditable) continue;
100
+ part.view.setProps({ editable: () => !getReadOnly() });
101
+ }
102
+ }
94
103
  if (usesCanonicalSession(deps.getExperimentalSession?.(), CANONICAL_GAP.authorityRouting) && !deps.getCanonicalApi?.()?.getCanonicalStoryProjection(OP_STORIES.MAIN)) return;
95
104
  const document = getOwnedDocument();
96
105
  const styles = deps.getStyles();
@@ -179,11 +188,13 @@ const createHeaderFooterEditorManager = (deps) => {
179
188
  }
180
189
  const view = (deps.createView ?? createEditorView)(mountNode, {
181
190
  ...canonical.props,
191
+ editable: () => !(deps.getReadOnly?.() ?? false),
182
192
  state: canonicalProjection ? EditorState.create({
183
193
  doc: canonicalProjection,
184
194
  plugins: [createDocumentStyleContextPlugin(styles), createDocumentNumberingPlugin(numbering)]
185
195
  }) : buildInitialState(headerFooter, styles, theme, numbering, manager),
186
196
  dispatchTransaction(transaction) {
197
+ if ((deps.getReadOnly?.() ?? false) && transaction.docChanged) return;
187
198
  if (canonical.dispatch(transaction)) return;
188
199
  const nextState = view.state.apply(transaction);
189
200
  const docChanged = !nextState.doc.eq(view.state.doc);
@@ -1,10 +1,10 @@
1
1
  import { document_d_exports } from "../types/document.js";
2
2
  import { CanonicalGap } from "../types/canonicalCapabilities.js";
3
+ import { EditorMode } from "../managers/EditorModeManager.js";
3
4
  import { HiddenEditorApi } from "./hiddenEditorApi.js";
4
5
  import { HiddenEditorStateReason } from "../layout-engine/layoutInstrumentation.js";
5
6
  import { ExtensionManager } from "../prosemirror/extensions/ExtensionManager.js";
6
7
  import { RemoteSelection } from "../types/remote-selection.js";
7
- import { EditorMode } from "../managers/EditorModeManager.js";
8
8
  import { EditorState, Plugin, Transaction } from "prosemirror-state";
9
9
  import { EditorView } from "prosemirror-view";
10
10
  import * as Yjs from "yjs";
@@ -812,7 +812,9 @@ const createHiddenEditorManager = (deps) => {
812
812
  };
813
813
  const syncEditable = () => {
814
814
  if (!view) return;
815
- syncHiddenEditorAccessibility(view, deps.getReadOnly());
815
+ const readOnly = deps.getReadOnly();
816
+ if (view.editable !== !readOnly) view.setProps({ editable: () => !deps.getReadOnly() });
817
+ syncHiddenEditorAccessibility(view, readOnly);
816
818
  };
817
819
  const publishStoryCommit = (storyView, commit) => {
818
820
  if (!view || editorSession.type !== "canonical") return false;
@@ -1,8 +1,8 @@
1
1
  import { document_d_exports } from "../types/document.js";
2
2
  import { TemplatePreviewEntry, TemplatePreviewHiddenRange } from "../prosemirror/plugins/templatePreviewValues.js";
3
+ import { DisplayMode } from "../managers/EditorModeManager.js";
3
4
  import { ColumnLayout, FlowBlock, FootnoteContent, HeaderFooterContent, Layout, Measure, PageHeaderFooterRefs, PageMargins } from "../layout-engine/types.js";
4
5
  import { LayoutRunOptions } from "./layoutRunOptions.js";
5
- import { DisplayMode } from "../managers/EditorModeManager.js";
6
6
  import { LayoutSession } from "./layoutSession.js";
7
7
  import { HyphenationReadiness } from "./hyphenationReadiness.js";
8
8
  import { ConvertHeaderFooterOptions, HeaderFooterMetrics } from "../layout-bridge/convert/headerFooterLayout.js";
@@ -79,6 +79,8 @@ type LayoutPipelineDeps<THfPMs> = {
79
79
  * authored text with runs hidden afterwards.
80
80
  */
81
81
  markupView: DisplayMode;
82
+ /** Independent highlight visibility in All Markup; resolved views always hide comments. */
83
+ showCommentHighlights?: boolean;
82
84
  };
83
85
  declare function runLayoutPipeline<THfPMs>(deps: LayoutPipelineDeps<THfPMs>, state: EditorState, options?: LayoutRunOptions): LayoutOutcome;
84
86
  //#endregion
@@ -42,7 +42,7 @@ const formatEndnoteTexts = (numbers, formatNumber) => {
42
42
  for (const [id, displayNumber] of numbers) texts.set(id, formatNumber(displayNumber));
43
43
  return texts;
44
44
  };
45
- const sameMeasureInputs = (left, right) => left.embeddedFontFamilyMap === right.embeddedFontFamilyMap && left.styles === right.styles && left.theme === right.theme && left.defaultTabStop === right.defaultTabStop && left.pageContentHeight === right.pageContentHeight && left.fontSet === right.fontSet && left.fontAlternates.size === right.fontAlternates.size && [...left.fontAlternates].every(([name, alternate]) => right.fontAlternates.get(name) === alternate) && left.markupView === right.markupView;
45
+ const sameMeasureInputs = (left, right) => left.embeddedFontFamilyMap === right.embeddedFontFamilyMap && left.styles === right.styles && left.theme === right.theme && left.defaultTabStop === right.defaultTabStop && left.pageContentHeight === right.pageContentHeight && left.fontSet === right.fontSet && left.fontAlternates.size === right.fontAlternates.size && [...left.fontAlternates].every(([name, alternate]) => right.fontAlternates.get(name) === alternate) && left.markupView === right.markupView && left.showCommentHighlights === right.showCommentHighlights;
46
46
  /**
47
47
  * The range an incremental pass re-measures, or `null` for a full measure: the
48
48
  * committed measures must come from the same measure inputs, and the range is
@@ -136,7 +136,7 @@ function runLayoutPipeline(deps, state, options = {}) {
136
136
  return result;
137
137
  }
138
138
  function runLayoutPipelineMeasured(deps, state, options) {
139
- const { contentWidth, columns, pageSize, margins, pageGap, showMarginGuides, marginGuideColor, syncCoordinator, headerContent, footerContent, firstPageHeaderContent, firstPageFooterContent, headerContentRId, footerContentRId, firstPageHeaderContentRId, firstPageFooterContentRId, sectionHeaderFooterRefs, theme: _theme, sectionProperties, document, defaultTabStop, mirrorMargins, styles, layout, hfPMs, painter, pagesContainer, session, renderHfFromContentOrPm, renderHeaderFooterContentByRId, readFontSetSignature, buildFootnoteRenderItems, describeInvalidHighlightMarks, pageRenderer = PAGE_RENDERER.legacy, emptyTemplatePreviewEntries: EMPTY_TEMPLATE_PREVIEW_ENTRIES, emptyTemplatePreviewHidden: EMPTY_TEMPLATE_PREVIEW_HIDDEN, markupView } = deps;
139
+ const { contentWidth, columns, pageSize, margins, pageGap, showMarginGuides, marginGuideColor, syncCoordinator, headerContent, footerContent, firstPageHeaderContent, firstPageFooterContent, headerContentRId, footerContentRId, firstPageHeaderContentRId, firstPageFooterContentRId, sectionHeaderFooterRefs, theme: _theme, sectionProperties, document, defaultTabStop, mirrorMargins, styles, layout, hfPMs, painter, pagesContainer, session, renderHfFromContentOrPm, renderHeaderFooterContentByRId, readFontSetSignature, buildFootnoteRenderItems, describeInvalidHighlightMarks, pageRenderer = PAGE_RENDERER.legacy, emptyTemplatePreviewEntries: EMPTY_TEMPLATE_PREVIEW_ENTRIES, emptyTemplatePreviewHidden: EMPTY_TEMPLATE_PREVIEW_HIDDEN, markupView, showCommentHighlights = true } = deps;
140
140
  let outcome = {};
141
141
  let pendingArtifacts;
142
142
  let pendingTemplatePreview;
@@ -154,6 +154,7 @@ function runLayoutPipelineMeasured(deps, state, options) {
154
154
  let phaseStartedAt = performance.now();
155
155
  const pageContentHeight = pageSize.h - margins.top - margins.bottom;
156
156
  const fontAlternates = buildFontAlternates(document?.package.fontTable);
157
+ const displayCommentHighlights = showCommentHighlights && markupView !== "no-markup" && markupView !== "original";
157
158
  const embeddedFontFamilies = session.embeddedFontFamilyMap ? { embeddedFontFamilyMap: session.embeddedFontFamilyMap } : {};
158
159
  const measureInputs = {
159
160
  ...embeddedFontFamilies,
@@ -163,9 +164,11 @@ function runLayoutPipelineMeasured(deps, state, options) {
163
164
  pageContentHeight,
164
165
  fontSet,
165
166
  fontAlternates,
166
- markupView
167
+ markupView,
168
+ showCommentHighlights: displayCommentHighlights
167
169
  };
168
170
  const flowOpts = {
171
+ showCommentHighlights: displayCommentHighlights,
169
172
  ...embeddedFontFamilies,
170
173
  pageContentHeight,
171
174
  fontAlternates,
@@ -246,6 +249,8 @@ function runLayoutPipelineMeasured(deps, state, options) {
246
249
  const buildHfOptions = (pageCount, fieldInputs) => {
247
250
  const hfMeasureBlocks = (hfBlocks, hfWidth) => measureBlocks(hfBlocks, hfWidth, void 0, void 0, buildHeaderFooterFieldValues(hfBlocks, pageCount, hfClock, fieldInputs), { allowEndTabOverflow: true });
248
251
  return {
252
+ markupView,
253
+ showCommentHighlights: displayCommentHighlights,
249
254
  ...flowOpts.styles ? { styles: flowOpts.styles } : {},
250
255
  ...document?.package.numbering === void 0 ? {} : { numbering: document.package.numbering },
251
256
  ...flowOpts.defaultSize === void 0 ? {} : { defaultSize: flowOpts.defaultSize },
@@ -378,7 +383,11 @@ function runLayoutPipelineMeasured(deps, state, options) {
378
383
  outcome.measures = newMeasures;
379
384
  recordPhaseDuration("measure-blocks", phaseStartedAt);
380
385
  const buildNoteOptions = () => {
381
- const noteOptions = { measureBlocks };
386
+ const noteOptions = {
387
+ measureBlocks,
388
+ showCommentHighlights: displayCommentHighlights,
389
+ markupView
390
+ };
382
391
  if (flowOpts.styles) noteOptions.styles = flowOpts.styles;
383
392
  if (flowOpts.defaultSize !== void 0) noteOptions.defaultSize = flowOpts.defaultSize;
384
393
  if (_theme !== void 0) noteOptions.theme = _theme;
@@ -1,7 +1,7 @@
1
1
  import { document_d_exports } from "../types/document.js";
2
2
  import { TemplatePreviewEntry, TemplatePreviewHiddenRange, TemplatePreviewValues } from "../prosemirror/plugins/templatePreviewValues.js";
3
- import { FlowBlock, Measure } from "../layout-engine/types.js";
4
3
  import { DisplayMode } from "../managers/EditorModeManager.js";
4
+ import { FlowBlock, Measure } from "../layout-engine/types.js";
5
5
  import { FontAlternates } from "../fonts/fontAlternates.js";
6
6
  import { EditorState } from "prosemirror-state";
7
7
  //#region src/controller/layoutSession.d.ts
@@ -36,6 +36,7 @@ type LayoutMeasureInputs = {
36
36
  fontAlternates: FontAlternates;
37
37
  /** The review view: each lays out different text, so a measure is reusable only within one. */
38
38
  markupView: DisplayMode;
39
+ showCommentHighlights?: boolean;
39
40
  };
40
41
  type LayoutSession = {
41
42
  embeddedFontFamilyMap?: ReadonlyMap<string, string>;
@@ -1,5 +1,6 @@
1
1
  import { parseRotationDegrees } from "../../utils/rotationBoundingBox.js";
2
2
  import { sanitizeImageSrc } from "../../utils/sanitizeImageSrc.js";
3
+ import { base64ToBytes } from "../../utils/textEncoding.js";
3
4
  import { paintPreview } from "./previewPrimitives.js";
4
5
  import { resolveBorderStroke } from "./strokes.js";
5
6
  import { UNSUPPORTED_CONSTRUCT } from "./unsupported.js";
@@ -21,12 +22,8 @@ const DATA_URL_PATTERN = /^data:([^;,]*)(;base64)?,(.*)$/su;
21
22
  * document the builder reports, not an exception that aborts the build.
22
23
  */
23
24
  const decodeBase64 = (encoded) => {
24
- const decoded = Result.try(() => atob(encoded));
25
- if (decoded.isErr()) return;
26
- const binary = decoded.value;
27
- const bytes = new Uint8Array(binary.length);
28
- for (let index = 0; index < binary.length; index += 1) bytes[index] = binary.charCodeAt(index);
29
- return bytes;
25
+ const decoded = Result.try(() => base64ToBytes(encoded));
26
+ return decoded.isOk() ? decoded.value : void 0;
30
27
  };
31
28
  const readUint32BE = (bytes, offset) => bytes[offset] << 24 | bytes[offset + 1] << 16 | bytes[offset + 2] << 8 | bytes[offset + 3];
32
29
  const readUint16BE = (bytes, offset) => bytes[offset] << 8 | bytes[offset + 1];
@@ -1,5 +1,6 @@
1
1
  import { withTrackedSectionEndpointRemoval } from "../internal/sectionEndpointResolution.js";
2
2
  import { withSectionReferenceResolution } from "../internal/sectionReferenceResolution.js";
3
+ import { decodeText, encodeUtf8 } from "../utils/textEncoding.js";
3
4
  import { readDocumentSectionFacts } from "./documentSectionFacts.js";
4
5
  import { parseDocx } from "./parser.js";
5
6
  import { assertSectionCarriersMatchModel } from "./rezip.js";
@@ -9,7 +10,7 @@ import JSZip from "jszip";
9
10
  /** Canonical snapshots own explicit section references; the PM projection does not. */
10
11
  const referenceKey = ({ element, type, rId }) => `${element}:${type}:${rId}`;
11
12
  const xmlFingerprint = async (xml) => {
12
- const digest = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(xml));
13
+ const digest = await crypto.subtle.digest("SHA-256", encodeUtf8(xml));
13
14
  let fingerprint = "";
14
15
  for (const byte of new Uint8Array(digest)) fingerprint += byte.toString(16).padStart(2, "0");
15
16
  return fingerprint;
@@ -18,7 +19,7 @@ const xmlFingerprint = async (xml) => {
18
19
  const repackWithCanonicalStoryRemovals = async ({ document, repack }) => {
19
20
  const originalBuffer = document.originalBuffer;
20
21
  if (!originalBuffer) return repack();
21
- const xml = await (await JSZip.loadAsync(originalBuffer)).file("word/document.xml")?.async("text");
22
+ const xml = await (await JSZip.loadAsync(originalBuffer)).file("word/document.xml")?.async("uint8array").then((bytes) => decodeText(bytes, { ignoreBOM: true }));
22
23
  if (!xml) return repack();
23
24
  const before = readDocumentSectionFacts(xml);
24
25
  const serializedXml = serializeDocument(document);
@@ -0,0 +1,6 @@
1
+ import { document_d_exports } from "../types/document.js";
2
+ //#region src/docx/commentTextParagraph.d.ts
3
+ /** Plain comment text has no authored paragraph formatting. */
4
+ declare const commentTextParagraph: (text: string) => document_d_exports.Paragraph;
5
+ //#endregion
6
+ export { commentTextParagraph };