@stll/folio-core 0.52.0 → 0.53.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +23 -0
- package/dist/ai-edits/apply.d.ts +26 -1
- package/dist/ai-edits/apply.js +1519 -105
- package/dist/ai-edits/batch-claims.d.ts +130 -0
- package/dist/ai-edits/batch-claims.js +244 -0
- package/dist/ai-edits/blockRange.js +9 -3
- package/dist/ai-edits/character-boundaries.d.ts +43 -0
- package/dist/ai-edits/character-boundaries.js +95 -0
- package/dist/ai-edits/clean-text.d.ts +33 -3
- package/dist/ai-edits/clean-text.js +52 -6
- package/dist/ai-edits/comment-lifecycle.d.ts +18 -0
- package/dist/ai-edits/comment-lifecycle.js +52 -0
- package/dist/ai-edits/headless.d.ts +71 -7
- package/dist/ai-edits/headless.js +381 -41
- package/dist/ai-edits/index.d.ts +2 -1
- package/dist/ai-edits/index.js +2 -1
- package/dist/ai-edits/minimal-replacement.d.ts +21 -6
- package/dist/ai-edits/minimal-replacement.js +134 -15
- package/dist/ai-edits/newListNumbering.d.ts +23 -0
- package/dist/ai-edits/newListNumbering.js +94 -0
- package/dist/ai-edits/note-references.d.ts +96 -0
- package/dist/ai-edits/note-references.js +139 -0
- package/dist/ai-edits/pending-suggestions.d.ts +79 -0
- package/dist/ai-edits/pending-suggestions.js +306 -0
- package/dist/ai-edits/read.d.ts +1 -1
- package/dist/ai-edits/read.js +56 -43
- package/dist/ai-edits/result-validation.d.ts +29 -0
- package/dist/ai-edits/result-validation.js +164 -0
- package/dist/ai-edits/snapshot.d.ts +36 -3
- package/dist/ai-edits/snapshot.js +177 -23
- package/dist/ai-edits/table-cell-mutations.d.ts +1 -1
- package/dist/ai-edits/table-cell-mutations.js +4 -1
- package/dist/ai-edits/table-geometry.d.ts +1 -1
- package/dist/ai-edits/table-row-column-mutations.d.ts +38 -16
- package/dist/ai-edits/table-row-column-mutations.js +61 -26
- package/dist/ai-edits/types.d.ts +155 -16
- package/dist/compare/compare.d.ts +1 -1
- package/dist/compare/compare.js +65 -16
- package/dist/compare/content-alignment.js +13 -2
- package/dist/compare/content-types.d.ts +7 -0
- package/dist/compare/content.d.ts +4 -1
- package/dist/compare/content.js +6 -1
- package/dist/compare/formatting.js +2 -1
- package/dist/compare/inline-atoms.d.ts +21 -4
- package/dist/compare/inline-atoms.js +138 -16
- package/dist/compare/plan.js +10 -5
- package/dist/compare/scenario.js +14 -3
- package/dist/compare/section-boundary-properties.js +1 -1
- package/dist/compare/types.d.ts +4 -2
- package/dist/compare/types.js +2 -1
- package/dist/compare/verification.d.ts +1 -1
- package/dist/compare/verification.js +15 -3
- package/dist/controller/committedLayoutDiff.d.ts +25 -0
- package/dist/controller/committedLayoutDiff.js +71 -0
- package/dist/controller/contentControlWidgetController.d.ts +1 -1
- package/dist/controller/folioEditor.d.ts +1 -1
- package/dist/controller/fontReadiness.d.ts +70 -3
- package/dist/controller/fontReadiness.js +100 -10
- package/dist/controller/headerFooterEditorManager.js +6 -1
- package/dist/controller/layoutPipeline.d.ts +12 -10
- package/dist/controller/layoutPipeline.js +62 -9
- package/dist/controller/layoutRunOptions.d.ts +24 -0
- package/dist/controller/layoutRunOptions.js +17 -0
- package/dist/controller/layoutScheduler.d.ts +19 -14
- package/dist/controller/layoutScheduler.js +15 -12
- package/dist/controller/layoutSession.d.ts +26 -2
- package/dist/controller/layoutSession.js +1 -1
- package/dist/controller/noteEditorManager.js +8 -3
- package/dist/document-operations.d.ts +11 -2
- package/dist/document-operations.js +230 -21
- package/dist/docx/altChunk.d.ts +8 -0
- package/dist/docx/altChunk.js +17 -0
- package/dist/docx/attributeRemainder.d.ts +1 -1
- package/dist/docx/blockContentParser.d.ts +64 -1
- package/dist/docx/blockContentParser.js +17 -4
- package/dist/docx/blockCustomXmlShell.d.ts +7 -0
- package/dist/docx/blockCustomXmlShell.js +14 -0
- package/dist/docx/blockPlainText.js +7 -0
- package/dist/docx/commentAnchorIndex.js +1 -1
- package/dist/docx/commentRangeIntegrity.js +8 -3
- package/dist/docx/commentReferenceNormalization.js +1 -1
- package/dist/docx/commentReplyMarkers.js +20 -1
- package/dist/docx/compatibility.d.ts +1 -1
- package/dist/docx/compatibility.js +34 -13
- package/dist/docx/containerChildren.gen.d.ts +1 -1
- package/dist/docx/containerChildren.gen.js +1 -0
- package/dist/docx/documentParser.js +12 -12
- package/dist/docx/drawingIdNormalization.d.ts +3 -1
- package/dist/docx/drawingIdNormalization.js +6 -1
- package/dist/docx/ensureParaIds.js +119 -97
- package/dist/docx/footnoteParser.js +8 -4
- package/dist/docx/headerFooterParser.js +3 -3
- package/dist/docx/headerFooterReferenceNormalization.d.ts +20 -1
- package/dist/docx/headerFooterReferenceNormalization.js +52 -27
- package/dist/docx/hyperlinkParser.d.ts +1 -1
- package/dist/docx/index.d.ts +1 -1
- package/dist/docx/listNumberingInstances.d.ts +59 -0
- package/dist/docx/listNumberingInstances.js +249 -0
- package/dist/docx/normalizeBaseDirection.js +2 -1
- package/dist/docx/opaqueCarrier.d.ts +7 -0
- package/dist/docx/opaqueCarrier.js +38 -0
- package/dist/docx/packageParts.js +4 -1
- package/dist/docx/paragraphParser.js +91 -24
- package/dist/docx/paragraphPropertySource.d.ts +4 -2
- package/dist/docx/paragraphPropertySource.js +7 -1
- package/dist/docx/paragraphTraversal.js +1 -0
- package/dist/docx/parseWarningMessage.js +4 -1
- package/dist/docx/parser.js +173 -20
- package/dist/docx/rezip.js +27 -5
- package/dist/docx/sdtProperties.js +1 -1
- package/dist/docx/sdtPropertiesPatch.js +3 -0
- package/dist/docx/selectiveSave.js +16 -9
- package/dist/docx/selectiveXmlPatch.js +106 -37
- package/dist/docx/serializer/blockCustomXmlSerializer.d.ts +5 -0
- package/dist/docx/serializer/blockCustomXmlSerializer.js +4 -0
- package/dist/docx/serializer/documentSerializer.js +2 -0
- package/dist/docx/serializer/headerFooterSerializer.js +2 -0
- package/dist/docx/serializer/noteSerializer.js +3 -1
- package/dist/docx/serializer/paragraphSerializer.js +33 -9
- package/dist/docx/serializer/tableSerializer.js +25 -38
- package/dist/docx/server/createBilingualDocument.js +3 -3
- package/dist/docx/tableParser.d.ts +157 -1
- package/dist/docx/tableParser.js +89 -10
- package/dist/docx/textBoxParser.js +1 -1
- package/dist/docx/unzip.d.ts +2 -1
- package/dist/docx/unzip.js +1 -1
- package/dist/docx/wordprocessingPrefixes.d.ts +78 -0
- package/dist/docx/wordprocessingPrefixes.js +286 -0
- package/dist/i18n/messages/catalogs.gen.d.ts +136 -0
- package/dist/i18n/messages/catalogs.gen.js +153 -17
- package/dist/i18n/messages/messages.gen.d.ts +8 -0
- package/dist/internal/compare/inline-presentation.d.ts +2 -1
- package/dist/internal/compare/inline-presentation.js +11 -10
- package/dist/internal/headlessRevisionResolutionGuard.d.ts +2 -8
- package/dist/internal/headlessRevisionResolutionGuard.js +2 -11
- package/dist/internal/indexedPositionMap.d.ts +11 -0
- package/dist/internal/indexedPositionMap.js +42 -0
- package/dist/internal/pageBreakRunSourceDescendantIndex.js +2 -1
- package/dist/internal/revisionResolutionEdits.d.ts +13 -0
- package/dist/internal/revisionResolutionEdits.js +19 -0
- package/dist/internal/revisionResolutionInline.d.ts +24 -0
- package/dist/internal/{headlessRevisionResolution.js → revisionResolutionInline.js} +61 -43
- package/dist/internal/revisionResolutionStep.d.ts +34 -0
- package/dist/internal/revisionResolutionStep.js +282 -0
- package/dist/internal/revisionResolutionTracking.d.ts +11 -0
- package/dist/internal/revisionResolutionTracking.js +124 -0
- package/dist/internal/wholeStoryRevisionResolution.d.ts +56 -0
- package/dist/internal/wholeStoryRevisionResolution.js +530 -0
- package/dist/layout-bridge/convert/markupViewFlow.d.ts +20 -0
- package/dist/layout-bridge/convert/markupViewFlow.js +123 -0
- package/dist/layout-bridge/convert/tableConversion.js +1 -1
- package/dist/layout-bridge/convert/toFlowBlocks.js +10 -1
- package/dist/layout-engine/layoutInstrumentation.d.ts +9 -1
- package/dist/layout-engine/layoutInstrumentation.js +4 -1
- package/dist/layout-engine/measure/cache.d.ts +18 -1
- package/dist/layout-engine/measure/cache.js +31 -1
- package/dist/layout-engine/measure/font-metrics.worker.js +3 -7
- package/dist/layout-engine/measure/measureContainer.js +7 -47
- package/dist/layout-engine/measure/measureHelpers.d.ts +6 -1
- package/dist/layout-engine/measure/measureHelpers.js +22 -2
- package/dist/layout-engine/measure/measureWorker.js +2 -3
- package/dist/layout-engine/measure/measureWorkerProtocol.d.ts +6 -7
- package/dist/layout-engine/measure/measureWorkerProtocol.js +1 -2
- package/dist/layout-engine/types.d.ts +6 -0
- package/dist/layout-painter/renderPage.js +1 -0
- package/dist/layout-painter/renderParagraph.js +1 -1
- package/dist/managers/ContextMenuManager.js +5 -2
- package/dist/managers/types.d.ts +3 -0
- package/dist/markdown/fromMarkdown.js +2 -1
- package/dist/markdown/internals.d.ts +11 -1
- package/dist/markdown/internals.js +24 -3
- package/dist/markdown/renderBlock.js +39 -9
- package/dist/markdown/renderParagraph.d.ts +13 -1
- package/dist/markdown/renderParagraph.js +85 -38
- package/dist/markdown/renderRuns.js +3 -10
- package/dist/markdown/renderTable.js +24 -12
- package/dist/markdown/trailers.js +2 -2
- package/dist/markdown/types.d.ts +29 -7
- package/dist/paged-layout/incrementalMeasure.d.ts +1 -2
- package/dist/paged-layout/incrementalMeasure.js +1 -9
- package/dist/paged-layout/transactionDirtyRange.d.ts +1 -3
- package/dist/paged-layout/transactionDirtyRange.js +29 -41
- package/dist/panel-layout.d.ts +83 -0
- package/dist/panel-layout.js +96 -0
- package/dist/prosemirror/anchoredTextBoxes.d.ts +37 -0
- package/dist/prosemirror/anchoredTextBoxes.js +156 -0
- package/dist/prosemirror/attrs/index.d.ts +6 -2
- package/dist/prosemirror/attrs/index.js +172 -1
- package/dist/prosemirror/commands/comments.d.ts +7 -8
- package/dist/prosemirror/commands/comments.js +241 -207
- package/dist/prosemirror/commands/contentControls.d.ts +1 -1
- package/dist/prosemirror/commands/formatPainter.d.ts +1 -1
- package/dist/prosemirror/commands/formatting.d.ts +1 -1
- package/dist/prosemirror/commands/index.d.ts +2 -2
- package/dist/prosemirror/commands/index.js +2 -2
- package/dist/prosemirror/commands/paragraph.d.ts +7 -1
- package/dist/prosemirror/commands/paragraph.js +9 -1
- package/dist/prosemirror/commands/paragraphBookmarkJoin.d.ts +13 -0
- package/dist/prosemirror/commands/paragraphBookmarkJoin.js +22 -0
- package/dist/prosemirror/commands/pastePlainText.d.ts +1 -1
- package/dist/prosemirror/commands/resolveAllTableChanges.d.ts +40 -0
- package/dist/prosemirror/commands/resolveAllTableChanges.js +615 -0
- package/dist/prosemirror/commands/resolveNodePropertyChangeAttrs.d.ts +15 -0
- package/dist/prosemirror/commands/resolveNodePropertyChangeAttrs.js +46 -0
- package/dist/prosemirror/commands/resolveParagraphProperties.d.ts +16 -0
- package/dist/prosemirror/commands/resolveParagraphProperties.js +59 -0
- package/dist/prosemirror/commands/sectionBreak.js +2 -1
- package/dist/prosemirror/commands/tableCellMergeResolution.d.ts +8 -2
- package/dist/prosemirror/commands/tableCellMergeResolution.js +1 -1
- package/dist/prosemirror/contentControlRevisions.d.ts +1 -1
- package/dist/prosemirror/conversion/fromProseDoc.js +181 -31
- package/dist/prosemirror/conversion/toProseDoc.js +39 -4
- package/dist/prosemirror/emptyFieldResultRuns.d.ts +18 -0
- package/dist/prosemirror/emptyFieldResultRuns.js +33 -0
- package/dist/prosemirror/extensions/ExtensionManager.d.ts +1 -1
- package/dist/prosemirror/extensions/ExtensionManager.js +3 -1
- package/dist/prosemirror/extensions/StarterKit.js +3 -1
- package/dist/prosemirror/extensions/core/DocExtension.js +1 -1
- package/dist/prosemirror/extensions/core/ParagraphExtension.js +12 -0
- package/dist/prosemirror/extensions/features/BaseKeymapExtension.js +3 -0
- package/dist/prosemirror/extensions/features/ListExtension.js +161 -123
- package/dist/prosemirror/extensions/features/ParaIdAllocatorExtension.d.ts +19 -2
- package/dist/prosemirror/extensions/features/ParaIdAllocatorExtension.js +33 -20
- package/dist/prosemirror/extensions/features/ParagraphChangeTrackerExtension.d.ts +31 -2
- package/dist/prosemirror/extensions/features/ParagraphChangeTrackerExtension.js +270 -99
- package/dist/prosemirror/extensions/features/PasteCleanupExtension.js +3 -2
- package/dist/prosemirror/extensions/features/pasteCleanup.js +2 -1
- package/dist/prosemirror/extensions/features/pastedHtmlLists.d.ts +12 -0
- package/dist/prosemirror/extensions/features/pastedHtmlLists.js +126 -0
- package/dist/prosemirror/extensions/marks/FootnoteRefExtension.js +21 -5
- package/dist/prosemirror/extensions/marks/RunIdentityExtension.d.ts +21 -18
- package/dist/prosemirror/extensions/marks/RunIdentityExtension.js +37 -17
- package/dist/prosemirror/extensions/marks/markUtils.d.ts +7 -2
- package/dist/prosemirror/extensions/marks/markUtils.js +8 -2
- package/dist/prosemirror/extensions/marks/noteReferenceDeletion.d.ts +10 -0
- package/dist/prosemirror/extensions/marks/noteReferenceDeletion.js +35 -0
- package/dist/prosemirror/extensions/nodes/BlockCustomXmlExtension.d.ts +6 -0
- package/dist/prosemirror/extensions/nodes/BlockCustomXmlExtension.js +52 -0
- package/dist/prosemirror/extensions/nodes/FieldExtension.d.ts +3 -3
- package/dist/prosemirror/extensions/nodes/FieldExtension.js +50 -15
- package/dist/prosemirror/extensions/nodes/PreservedBlockExtension.js +4 -1
- package/dist/prosemirror/extensions/nodes/TableExtension.d.ts +1 -1
- package/dist/prosemirror/extensions/nodes/TableExtension.js +19 -18
- package/dist/prosemirror/extensions/types.d.ts +4 -1
- package/dist/prosemirror/findReplaceSelection.js +1 -1
- package/dist/prosemirror/index.d.ts +3 -2
- package/dist/prosemirror/index.js +3 -2
- package/dist/prosemirror/indexedNodeLookup.d.ts +17 -0
- package/dist/prosemirror/indexedNodeLookup.js +103 -0
- package/dist/prosemirror/listAutoformatMarkers.d.ts +19 -0
- package/dist/prosemirror/listAutoformatMarkers.js +98 -0
- package/dist/prosemirror/listInstanceReferences.d.ts +12 -0
- package/dist/prosemirror/listInstanceReferences.js +72 -0
- package/dist/prosemirror/listLabels.d.ts +24 -0
- package/dist/prosemirror/listLabels.js +111 -0
- package/dist/prosemirror/listNumbering.d.ts +110 -0
- package/dist/prosemirror/listNumbering.js +345 -0
- package/dist/prosemirror/listRenderingAttrs.d.ts +11 -2
- package/dist/prosemirror/listRenderingAttrs.js +8 -1
- package/dist/prosemirror/markupViewProjection.d.ts +95 -0
- package/dist/prosemirror/markupViewProjection.js +83 -0
- package/dist/prosemirror/numberingAttr.d.ts +9 -1
- package/dist/prosemirror/numberingAttr.js +9 -1
- package/dist/prosemirror/outlineLevelAttr.js +1 -1
- package/dist/prosemirror/paragraphIndentation.d.ts +10 -1
- package/dist/prosemirror/paragraphIndentation.js +29 -7
- package/dist/prosemirror/paragraphMarkJoin.d.ts +19 -0
- package/dist/prosemirror/paragraphMarkJoin.js +24 -0
- package/dist/prosemirror/plugins/createDocScanPlugin.d.ts +1 -1
- package/dist/prosemirror/plugins/documentNumbering.d.ts +27 -1
- package/dist/prosemirror/plugins/documentNumbering.js +97 -11
- package/dist/prosemirror/plugins/documentStyles.d.ts +10 -1
- package/dist/prosemirror/plugins/documentStyles.js +14 -1
- package/dist/prosemirror/plugins/suggestionMode.d.ts +16 -2
- package/dist/prosemirror/plugins/suggestionMode.js +116 -52
- package/dist/prosemirror/plugins/templateDirectives.d.ts +1 -1
- package/dist/prosemirror/plugins/transactionInvariants.d.ts +32 -0
- package/dist/prosemirror/plugins/transactionInvariants.js +86 -0
- package/dist/prosemirror/positionSweep.d.ts +24 -0
- package/dist/prosemirror/positionSweep.js +222 -0
- package/dist/prosemirror/rebaseParagraphRunFormatting.d.ts +1 -1
- package/dist/prosemirror/rejoinRunCarriers.d.ts +1 -1
- package/dist/prosemirror/replacedAnnotations.d.ts +33 -1
- package/dist/prosemirror/replacedAnnotations.js +170 -1
- package/dist/prosemirror/runFormattingInlineCarriers.d.ts +1 -1
- package/dist/prosemirror/schema/index.d.ts +2 -2
- package/dist/prosemirror/schema/index.js +3 -0
- package/dist/prosemirror/schema/nodes.d.ts +42 -11
- package/dist/prosemirror/sectionMarkEdits.d.ts +20 -0
- package/dist/prosemirror/sectionMarkEdits.js +59 -0
- package/dist/prosemirror/storyListNumbering.d.ts +17 -0
- package/dist/prosemirror/storyListNumbering.js +102 -0
- package/dist/prosemirror/tableGridMutation.d.ts +48 -2
- package/dist/prosemirror/tableGridMutation.js +106 -1
- package/dist/prosemirror/validation.js +5 -1
- package/dist/redline.js +93 -9
- package/dist/render-dom/BodySelectionOverlay.d.ts +4 -1
- package/dist/render-dom/BodySelectionOverlay.js +3 -2
- package/dist/utils/findReplace.js +2 -2
- package/dist/utils/mergeDocumentContent.js +1 -1
- package/dist/utils/noteReferenceLabels.d.ts +36 -0
- package/dist/utils/noteReferenceLabels.js +32 -0
- package/dist/utils/replaceText.js +1 -1
- package/dist/version-comparison.d.ts +1 -0
- package/dist/version-comparison.js +1 -0
- package/package.json +3 -2
- package/dist/internal/headlessRevisionResolution.d.ts +0 -23
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import { validateFolioDocumentModel } from "../docx/modelValidation.js";
|
|
2
|
+
import { proseDocToBlocks } from "../prosemirror/conversion/fromProseDoc.js";
|
|
3
|
+
import { sectionPropertiesOf } from "../prosemirror/sectionCarrier.js";
|
|
4
|
+
import { anchoredCommentIdsInProseDoc } from "./comment-lifecycle.js";
|
|
5
|
+
import { Fragment } from "prosemirror-model";
|
|
6
|
+
//#region src/ai-edits/result-validation.ts
|
|
7
|
+
/**
|
|
8
|
+
* The check an operation batch's result passes before it is committed.
|
|
9
|
+
*
|
|
10
|
+
* An operation that reaches the document is a promise that the document can
|
|
11
|
+
* still be saved: the batch reports it as applied, issues a receipt and an
|
|
12
|
+
* undo handle, and the next thing the caller does may be `save()`. The model
|
|
13
|
+
* validator the save runs (`validateFolioDocumentModel`, the one
|
|
14
|
+
* `repackDocx` asserts) is therefore the judge here too, run on what the
|
|
15
|
+
* batch produced, while the batch can still be refused.
|
|
16
|
+
*
|
|
17
|
+
* Two constraints shape it:
|
|
18
|
+
*
|
|
19
|
+
* - Cost. Converting a whole story back to the model and validating it costs
|
|
20
|
+
* about as much as the apply itself on a long document. A batch only
|
|
21
|
+
* changes the top-level blocks it touched, and ProseMirror keeps every
|
|
22
|
+
* untouched child node by identity, so only the window between the first
|
|
23
|
+
* and the last changed child is converted and validated.
|
|
24
|
+
* - Blame. The apply layer holds the story, not the package: comments, notes
|
|
25
|
+
* and header/footer parts are the host's. A window can therefore report an
|
|
26
|
+
* error the batch did not cause (a comment the package defines, a range the
|
|
27
|
+
* window cuts). Only an error the same window did not already have before
|
|
28
|
+
* the batch counts; an error the document already carried is the
|
|
29
|
+
* document's, and refusing every later edit near it would help no one.
|
|
30
|
+
*/
|
|
31
|
+
/**
|
|
32
|
+
* The top-level children that differ between two versions of one story,
|
|
33
|
+
* found by node identity from both ends. `null` when every child is shared.
|
|
34
|
+
*/
|
|
35
|
+
const changedTopLevelWindow = (before, after) => {
|
|
36
|
+
if (before === after) return null;
|
|
37
|
+
const beforeCount = before.childCount;
|
|
38
|
+
const afterCount = after.childCount;
|
|
39
|
+
let from = 0;
|
|
40
|
+
while (from < beforeCount && from < afterCount && before.child(from) === after.child(from)) from += 1;
|
|
41
|
+
let beforeTo = beforeCount;
|
|
42
|
+
let afterTo = afterCount;
|
|
43
|
+
while (beforeTo > from && afterTo > from && before.child(beforeTo - 1) === after.child(afterTo - 1)) {
|
|
44
|
+
beforeTo -= 1;
|
|
45
|
+
afterTo -= 1;
|
|
46
|
+
}
|
|
47
|
+
if (from === beforeTo && from === afterTo) return null;
|
|
48
|
+
return {
|
|
49
|
+
from,
|
|
50
|
+
beforeTo,
|
|
51
|
+
afterTo
|
|
52
|
+
};
|
|
53
|
+
};
|
|
54
|
+
const TOP_LEVEL_PATH = /^package\.document\.content\[(\d+)\]/u;
|
|
55
|
+
/** Rebase a window-relative block path onto the story's own block index. */
|
|
56
|
+
const rebasePath = (path, offset) => path.replace(TOP_LEVEL_PATH, (_match, index) => `package.document.content[${String(Number(index) + offset)}]`);
|
|
57
|
+
const syntheticComment = (id, author) => ({
|
|
58
|
+
id,
|
|
59
|
+
author,
|
|
60
|
+
content: []
|
|
61
|
+
});
|
|
62
|
+
/** A story can identify its existing references even though the host owns the parts. */
|
|
63
|
+
const knownSectionParts = (doc) => {
|
|
64
|
+
const headers = /* @__PURE__ */ new Map();
|
|
65
|
+
const footers = /* @__PURE__ */ new Map();
|
|
66
|
+
doc.descendants((node) => {
|
|
67
|
+
if (node.type.name !== "paragraph") return true;
|
|
68
|
+
const section = sectionPropertiesOf(node);
|
|
69
|
+
for (const { rId, type } of section?.headerReferences ?? []) headers.set(rId, {
|
|
70
|
+
type: "header",
|
|
71
|
+
hdrFtrType: type,
|
|
72
|
+
content: []
|
|
73
|
+
});
|
|
74
|
+
for (const { rId, type } of section?.footerReferences ?? []) footers.set(rId, {
|
|
75
|
+
type: "footer",
|
|
76
|
+
hdrFtrType: type,
|
|
77
|
+
content: []
|
|
78
|
+
});
|
|
79
|
+
return false;
|
|
80
|
+
});
|
|
81
|
+
return {
|
|
82
|
+
headers,
|
|
83
|
+
footers
|
|
84
|
+
};
|
|
85
|
+
};
|
|
86
|
+
const windowErrors = ({ doc, from, to, context, knownParts, knownCommentIds }) => {
|
|
87
|
+
if (to <= from) return [];
|
|
88
|
+
const children = [];
|
|
89
|
+
for (let index = from; index < to; index += 1) children.push(doc.child(index));
|
|
90
|
+
let content;
|
|
91
|
+
try {
|
|
92
|
+
content = proseDocToBlocks(doc.copy(Fragment.fromArray(children)), []);
|
|
93
|
+
} catch (error) {
|
|
94
|
+
return [{
|
|
95
|
+
path: `package.document.content[${String(from)}]`,
|
|
96
|
+
message: `The content cannot be converted for saving: ${error instanceof Error ? error.message : String(error)}`,
|
|
97
|
+
severity: "error"
|
|
98
|
+
}];
|
|
99
|
+
}
|
|
100
|
+
return validateFolioDocumentModel({ package: {
|
|
101
|
+
document: {
|
|
102
|
+
content,
|
|
103
|
+
comments: [...[...knownCommentIds ?? []].map((id) => syntheticComment(id, "")), ...context.createdCommentIds.filter((id) => !knownCommentIds?.has(id)).map((id) => syntheticComment(id, context.commentAuthor ?? ""))]
|
|
104
|
+
},
|
|
105
|
+
...context.numbering !== null && context.numbering !== void 0 && { numbering: context.numbering },
|
|
106
|
+
...knownParts && {
|
|
107
|
+
headers: knownParts.headers,
|
|
108
|
+
footers: knownParts.footers
|
|
109
|
+
}
|
|
110
|
+
} }).issues.filter((issue) => issue.severity === "error" && !(context.numbering === void 0 && issue.path.endsWith(".numPr.numId"))).map(({ path, message, severity }) => ({
|
|
111
|
+
path: rebasePath(path, from),
|
|
112
|
+
message,
|
|
113
|
+
severity
|
|
114
|
+
}));
|
|
115
|
+
};
|
|
116
|
+
/**
|
|
117
|
+
* The save-validator errors the batch introduced: errors in the changed window
|
|
118
|
+
* of `after` that the same window of `before` did not already report. Empty
|
|
119
|
+
* when the result is as saveable as the story it started from.
|
|
120
|
+
*/
|
|
121
|
+
const findIntroducedModelErrors = (before, after, context) => {
|
|
122
|
+
const window = changedTopLevelWindow(before, after);
|
|
123
|
+
if (window === null) return [];
|
|
124
|
+
let afterErrors = windowErrors({
|
|
125
|
+
doc: after,
|
|
126
|
+
from: window.from,
|
|
127
|
+
to: window.afterTo,
|
|
128
|
+
context
|
|
129
|
+
});
|
|
130
|
+
let parts;
|
|
131
|
+
if (afterErrors.some(({ message }) => message.startsWith("Section references missing "))) parts = knownSectionParts(before);
|
|
132
|
+
let knownCommentIds;
|
|
133
|
+
if (afterErrors.some(({ message }) => /^Comment \d+ is referenced but not present/u.test(message))) knownCommentIds = anchoredCommentIdsInProseDoc(before);
|
|
134
|
+
if (parts || knownCommentIds) afterErrors = windowErrors({
|
|
135
|
+
doc: after,
|
|
136
|
+
from: window.from,
|
|
137
|
+
to: window.afterTo,
|
|
138
|
+
context,
|
|
139
|
+
...parts !== void 0 && { knownParts: parts },
|
|
140
|
+
...knownCommentIds !== void 0 && { knownCommentIds }
|
|
141
|
+
});
|
|
142
|
+
if (afterErrors.length === 0) return afterErrors;
|
|
143
|
+
const known = /* @__PURE__ */ new Map();
|
|
144
|
+
for (const { message } of windowErrors({
|
|
145
|
+
doc: before,
|
|
146
|
+
from: window.from,
|
|
147
|
+
to: window.beforeTo,
|
|
148
|
+
context: {
|
|
149
|
+
...context,
|
|
150
|
+
createdCommentIds: []
|
|
151
|
+
},
|
|
152
|
+
...parts !== void 0 && { knownParts: parts },
|
|
153
|
+
...knownCommentIds !== void 0 && { knownCommentIds }
|
|
154
|
+
})) known.set(message, (known.get(message) ?? 0) + 1);
|
|
155
|
+
return afterErrors.filter(({ message }) => {
|
|
156
|
+
const remaining = known.get(message) ?? 0;
|
|
157
|
+
if (remaining === 0) return true;
|
|
158
|
+
known.set(message, remaining - 1);
|
|
159
|
+
return false;
|
|
160
|
+
});
|
|
161
|
+
};
|
|
162
|
+
const describeModelError = ({ path, message }) => `The result would not save: ${path}: ${message}`;
|
|
163
|
+
//#endregion
|
|
164
|
+
export { describeModelError, findIntroducedModelErrors };
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { FolioAIBlock, FolioAIBlockStructuralBoundary, FolioAIEditSnapshot, FolioAITextRangeHandle } from "./types.js";
|
|
2
|
+
import { NoteReferenceLabels } from "./note-references.js";
|
|
2
3
|
import { CleanBlockText } from "./clean-text.js";
|
|
3
4
|
import { RunStyleResolver } from "../prosemirror/runStyleFormatting.js";
|
|
4
5
|
import { Node } from "prosemirror-model";
|
|
@@ -67,10 +68,12 @@ declare const isFolioAIContentBlock: ({ text }: Pick<FolioAIBlock, "text">) => b
|
|
|
67
68
|
declare const trailingBodyBlockId: ({ blocks }: FolioAIEditSnapshot) => string | null;
|
|
68
69
|
declare const hashFolioAIBlockText: (text: string) => string;
|
|
69
70
|
/**
|
|
70
|
-
* Canonical public projection of the clean view's
|
|
71
|
+
* Canonical public projection of the clean view's inline structure.
|
|
71
72
|
*
|
|
72
73
|
* A field boundary stays internal: it marks text the reader already sees, so it
|
|
73
|
-
* belongs to range resolution rather than to a block's published structure.
|
|
74
|
+
* belongs to range resolution rather than to a block's published structure. A
|
|
75
|
+
* note reference is published: its marker reads like text, and a reader has to
|
|
76
|
+
* be able to tell it is not.
|
|
74
77
|
*/
|
|
75
78
|
declare const projectFolioAIBlockStructuralBoundaries: ({ structuralBoundaries }: Pick<CleanBlockText, "structuralBoundaries">) => readonly FolioAIBlockStructuralBoundary[];
|
|
76
79
|
/** Stable precondition fingerprint for a block's zero-width structure. */
|
|
@@ -90,6 +93,36 @@ declare const createFolioAITextRangeHandle: ({ blockId, text, startOffset, endOf
|
|
|
90
93
|
* whole subtree, so a table nested inside a hidden row is hidden with it.
|
|
91
94
|
*/
|
|
92
95
|
declare const isHiddenTableRow: (node: Node) => boolean;
|
|
96
|
+
/**
|
|
97
|
+
* The marker every footnote/endnote reference of a story reads as: numbered in
|
|
98
|
+
* reading order over the blocks {@link createFolioAIEditSnapshot} reads (a
|
|
99
|
+
* hidden row's references are as absent as its text), so the applier, the
|
|
100
|
+
* range resolvers and the snapshot all read the same text.
|
|
101
|
+
*
|
|
102
|
+
* The story is numbered on the first label asked for, not here: a reader
|
|
103
|
+
* whose walk meets no reference (most documents, and every revision-stats
|
|
104
|
+
* read) never pays for a second walk of the story.
|
|
105
|
+
*/
|
|
106
|
+
declare const collectNoteReferenceLabels: (doc: Node) => NoteReferenceLabels;
|
|
107
|
+
/**
|
|
108
|
+
* @internal The markers a comparison reads a revised story's references as:
|
|
109
|
+
* each note the base story references keeps the base's marker, and a note
|
|
110
|
+
* only the revised story references is numbered after them.
|
|
111
|
+
*
|
|
112
|
+
* Each story's own reading-order numbering would renumber every later marker
|
|
113
|
+
* of the revised story as soon as a reference is added or removed before it,
|
|
114
|
+
* and a text comparison would then read a reference both stories keep as an
|
|
115
|
+
* edit of it. Numbering the revised story on from the base keeps a kept
|
|
116
|
+
* reference's marker identical, and makes a reference the comparison brings
|
|
117
|
+
* write the marker the base story (which the edits resolve against) gives it.
|
|
118
|
+
*/
|
|
119
|
+
declare const alignedNoteReferenceLabels: (base: Node, revised: Node) => NoteReferenceLabels;
|
|
120
|
+
/**
|
|
121
|
+
* @internal The same snapshot with every note reference read through `labels`
|
|
122
|
+
* rather than the story's own numbering. Only the text and structural
|
|
123
|
+
* boundaries can change: block ids, anchors and the source document do not.
|
|
124
|
+
*/
|
|
125
|
+
declare const relabelFolioAIEditSnapshotNoteReferences: (snapshot: FolioAIEditSnapshot, labels: NoteReferenceLabels) => FolioAIEditSnapshot;
|
|
93
126
|
/** One table of a story, numbered the way {@link createFolioAIEditSnapshot} numbers it. */
|
|
94
127
|
type FolioStoryTable = {
|
|
95
128
|
/** Document-order index over every table, nested ones included. */
|
|
@@ -110,4 +143,4 @@ declare const createFolioAIEditSnapshot: (doc: Node) => FolioAIEditSnapshot;
|
|
|
110
143
|
/** @internal Use for an EditorState that owns the document's style resolver. */
|
|
111
144
|
declare const createFolioAIEditSnapshotWithStyleResolver: (doc: Node, styleResolver: RunStyleResolver | null) => FolioAIEditSnapshot;
|
|
112
145
|
//#endregion
|
|
113
|
-
export { FolioStoryTable, createFolioAIEditSnapshot, createFolioAIEditSnapshotWithStyleResolver, createFolioAITextRangeHandle, detachFolioAIEditSnapshotExternalHyperlinks, folioStoryTables, hashFolioAIBlockStructuralBoundaries, hashFolioAIBlockText, isFolioAIContentBlock, isHiddenTableRow, normalizeFolioAIBlockText, numberingReferenceKeysOf, projectFolioAIBlockStructuralBoundaries, remapFolioAIEditSnapshotNumberingReferences, remapFolioAIEditSnapshotStyleReferences, sourceDocumentOf, storyTablesOf, styleResolverOf, trailingBodyBlockId };
|
|
146
|
+
export { FolioStoryTable, alignedNoteReferenceLabels, collectNoteReferenceLabels, createFolioAIEditSnapshot, createFolioAIEditSnapshotWithStyleResolver, createFolioAITextRangeHandle, detachFolioAIEditSnapshotExternalHyperlinks, folioStoryTables, hashFolioAIBlockStructuralBoundaries, hashFolioAIBlockText, isFolioAIContentBlock, isHiddenTableRow, normalizeFolioAIBlockText, numberingReferenceKeysOf, projectFolioAIBlockStructuralBoundaries, relabelFolioAIEditSnapshotNoteReferences, remapFolioAIEditSnapshotNumberingReferences, remapFolioAIEditSnapshotStyleReferences, sourceDocumentOf, storyTablesOf, styleResolverOf, trailingBodyBlockId };
|
|
@@ -1,8 +1,11 @@
|
|
|
1
|
+
import { isAltChunkMarkup } from "../docx/altChunk.js";
|
|
1
2
|
import { EMPTY_BUILT_IN_STYLE_INDEX, resolveHeadingLevel } from "../docx/builtInStyles.js";
|
|
2
3
|
import { resolveParagraphNumbering } from "../docx/numberingReference.js";
|
|
4
|
+
import { OPAQUE_REVISION_CARRIER_READER_DIAGNOSTIC, isOpaqueNestedRowMarkup, opaqueRevisionCarrierName } from "../docx/opaqueCarrier.js";
|
|
3
5
|
import { recreateProseNodeWithParagraphPropertySource } from "../docx/paragraphPropertySource.js";
|
|
4
6
|
import { expectCharacterStyleMarkAttrs, expectHyperlinkMarkAttrs, expectParagraphAttrs, expectRunFormattingOverrideMarkAttrs } from "../prosemirror/attrs/index.js";
|
|
5
7
|
import { marksToTextFormatting } from "../prosemirror/conversion/fromProseDoc.js";
|
|
8
|
+
import { createListLabelCounter } from "../prosemirror/listLabels.js";
|
|
6
9
|
import { paragraphNumberingAttr, readParagraphNumberingAttr } from "../prosemirror/numberingAttr.js";
|
|
7
10
|
import { readOutlineLevelAttr } from "../prosemirror/outlineLevelAttr.js";
|
|
8
11
|
import { directParagraphAlignment } from "../prosemirror/paragraphAlignment.js";
|
|
@@ -11,9 +14,11 @@ import { directParagraphSpacing } from "../prosemirror/paragraphSpacing.js";
|
|
|
11
14
|
import { runFormattingInlineAtomCleanText } from "../prosemirror/runFormattingInlineCarriers.js";
|
|
12
15
|
import { authoredRunFormattingFromAttrs } from "../prosemirror/runFormattingProvenance.js";
|
|
13
16
|
import { readAuthoredRunFormatting, reconcileRunFormattingMarks } from "../prosemirror/runFormattingReconciliation.js";
|
|
14
|
-
import { paragraphRunStyleContext } from "../prosemirror/runStyleFormatting.js";
|
|
17
|
+
import { paragraphRunStyleContext, resolveParagraphBodyRunFormatting } from "../prosemirror/runStyleFormatting.js";
|
|
15
18
|
import { deriveBlankBlockId, deriveBlockId } from "../types/block-id.js";
|
|
19
|
+
import { splitsSurrogatePair } from "./character-boundaries.js";
|
|
16
20
|
import { buildCleanBlockText } from "./clean-text.js";
|
|
21
|
+
import { createNoteReferenceLabeler } from "./note-references.js";
|
|
17
22
|
import { panic } from "better-result";
|
|
18
23
|
import { Fragment } from "prosemirror-model";
|
|
19
24
|
import { TableMap } from "prosemirror-tables";
|
|
@@ -234,14 +239,25 @@ const hashFolioAIBlockText = (text) => {
|
|
|
234
239
|
const EMPTY_FOLIO_AI_BLOCK_STRUCTURAL_BOUNDARIES = Object.freeze([]);
|
|
235
240
|
const EMPTY_FOLIO_AI_BLOCK_STRUCTURAL_BOUNDARY_HASH = hashFolioAIBlockText(JSON.stringify(EMPTY_FOLIO_AI_BLOCK_STRUCTURAL_BOUNDARIES));
|
|
236
241
|
/**
|
|
237
|
-
* Canonical public projection of the clean view's
|
|
242
|
+
* Canonical public projection of the clean view's inline structure.
|
|
238
243
|
*
|
|
239
244
|
* A field boundary stays internal: it marks text the reader already sees, so it
|
|
240
|
-
* belongs to range resolution rather than to a block's published structure.
|
|
245
|
+
* belongs to range resolution rather than to a block's published structure. A
|
|
246
|
+
* note reference is published: its marker reads like text, and a reader has to
|
|
247
|
+
* be able to tell it is not.
|
|
241
248
|
*/
|
|
242
249
|
const projectFolioAIBlockStructuralBoundaries = ({ structuralBoundaries }) => {
|
|
243
250
|
let projected;
|
|
244
251
|
for (const boundary of structuralBoundaries) {
|
|
252
|
+
if (boundary.type === "noteReference") {
|
|
253
|
+
(projected ??= []).push({
|
|
254
|
+
type: "noteReference",
|
|
255
|
+
noteType: boundary.noteType,
|
|
256
|
+
offset: boundary.offset,
|
|
257
|
+
length: boundary.length
|
|
258
|
+
});
|
|
259
|
+
continue;
|
|
260
|
+
}
|
|
245
261
|
if (boundary.type !== "pageBreakRun" || !boundary.presentInCleanView) continue;
|
|
246
262
|
(projected ??= []).push({
|
|
247
263
|
type: "pageBreak",
|
|
@@ -255,7 +271,7 @@ const hashFolioAIBlockStructuralBoundaryProjection = (structuralBoundaries) => s
|
|
|
255
271
|
/** Stable precondition fingerprint for a block's zero-width structure. */
|
|
256
272
|
const hashFolioAIBlockStructuralBoundaries = (cleanBlock) => hashFolioAIBlockStructuralBoundaryProjection(projectFolioAIBlockStructuralBoundaries(cleanBlock));
|
|
257
273
|
const createFolioAITextRangeHandle = ({ blockId, text, startOffset, endOffset }) => {
|
|
258
|
-
if (blockId.length === 0 || !Number.isInteger(startOffset) || !Number.isInteger(endOffset) || startOffset < 0 || endOffset <= startOffset || endOffset > text.length) return null;
|
|
274
|
+
if (blockId.length === 0 || !Number.isInteger(startOffset) || !Number.isInteger(endOffset) || startOffset < 0 || endOffset <= startOffset || endOffset > text.length || splitsSurrogatePair(text, startOffset) || splitsSurrogatePair(text, endOffset)) return null;
|
|
259
275
|
return {
|
|
260
276
|
type: "textRange",
|
|
261
277
|
story: "main",
|
|
@@ -276,6 +292,64 @@ const TABLE_ROLE_ROW = "row";
|
|
|
276
292
|
* whole subtree, so a table nested inside a hidden row is hidden with it.
|
|
277
293
|
*/
|
|
278
294
|
const isHiddenTableRow = (node) => node.type.name === TABLE_ROW_NODE_NAME && node.attrs["hidden"] === true;
|
|
295
|
+
const noteReferenceLabelsByStory = /* @__PURE__ */ new WeakMap();
|
|
296
|
+
/**
|
|
297
|
+
* The marker every footnote/endnote reference of a story reads as: numbered in
|
|
298
|
+
* reading order over the blocks {@link createFolioAIEditSnapshot} reads (a
|
|
299
|
+
* hidden row's references are as absent as its text), so the applier, the
|
|
300
|
+
* range resolvers and the snapshot all read the same text.
|
|
301
|
+
*
|
|
302
|
+
* The story is numbered on the first label asked for, not here: a reader
|
|
303
|
+
* whose walk meets no reference (most documents, and every revision-stats
|
|
304
|
+
* read) never pays for a second walk of the story.
|
|
305
|
+
*/
|
|
306
|
+
const collectNoteReferenceLabels = (doc) => {
|
|
307
|
+
const cached = noteReferenceLabelsByStory.get(doc);
|
|
308
|
+
if (cached !== void 0) return cached;
|
|
309
|
+
const labels = numberNoteReferencesLazily([doc]);
|
|
310
|
+
noteReferenceLabelsByStory.set(doc, labels);
|
|
311
|
+
return labels;
|
|
312
|
+
};
|
|
313
|
+
/** Number the stories' references in reading order, one story after another, on first use. */
|
|
314
|
+
const numberNoteReferencesLazily = (stories) => {
|
|
315
|
+
let numbered;
|
|
316
|
+
const numberStories = () => {
|
|
317
|
+
const labeler = createNoteReferenceLabeler();
|
|
318
|
+
for (const doc of stories) doc.descendants((node) => {
|
|
319
|
+
if (isHiddenTableRow(node)) return false;
|
|
320
|
+
if (node.isTextblock) {
|
|
321
|
+
labeler.numberBlock(node);
|
|
322
|
+
return false;
|
|
323
|
+
}
|
|
324
|
+
return true;
|
|
325
|
+
});
|
|
326
|
+
return labeler;
|
|
327
|
+
};
|
|
328
|
+
return { labelOf: (reference) => (numbered ??= numberStories()).labelOf(reference) };
|
|
329
|
+
};
|
|
330
|
+
/**
|
|
331
|
+
* @internal The markers a comparison reads a revised story's references as:
|
|
332
|
+
* each note the base story references keeps the base's marker, and a note
|
|
333
|
+
* only the revised story references is numbered after them.
|
|
334
|
+
*
|
|
335
|
+
* Each story's own reading-order numbering would renumber every later marker
|
|
336
|
+
* of the revised story as soon as a reference is added or removed before it,
|
|
337
|
+
* and a text comparison would then read a reference both stories keep as an
|
|
338
|
+
* edit of it. Numbering the revised story on from the base keeps a kept
|
|
339
|
+
* reference's marker identical, and makes a reference the comparison brings
|
|
340
|
+
* write the marker the base story (which the edits resolve against) gives it.
|
|
341
|
+
*/
|
|
342
|
+
const alignedNoteReferenceLabels = (base, revised) => numberNoteReferencesLazily([base, revised]);
|
|
343
|
+
/**
|
|
344
|
+
* @internal The same snapshot with every note reference read through `labels`
|
|
345
|
+
* rather than the story's own numbering. Only the text and structural
|
|
346
|
+
* boundaries can change: block ids, anchors and the source document do not.
|
|
347
|
+
*/
|
|
348
|
+
const relabelFolioAIEditSnapshotNoteReferences = (snapshot, labels) => {
|
|
349
|
+
const metadata = metadataOf(snapshot);
|
|
350
|
+
if (!snapshot.blocks.some((block) => block.structuralBoundaries?.some(({ type }) => type === "noteReference"))) return snapshot;
|
|
351
|
+
return createFolioAIEditSnapshotInternal(metadata.sourceDocument, metadata.styleResolver, labels);
|
|
352
|
+
};
|
|
279
353
|
const STORY_TABLE_CONTAINER = "container";
|
|
280
354
|
const STORY_TABLE_HIDDEN_SUBTREE = "hidden-subtree";
|
|
281
355
|
const STORY_TABLE = "table";
|
|
@@ -336,12 +410,17 @@ const getTableLocation = ({ path, blockIndex, tableIndexByStart }) => {
|
|
|
336
410
|
};
|
|
337
411
|
}
|
|
338
412
|
};
|
|
339
|
-
const createFolioAIEditSnapshotInternal = (doc, styleResolver) => {
|
|
413
|
+
const createFolioAIEditSnapshotInternal = (doc, styleResolver, noteReferenceLabels) => {
|
|
340
414
|
const builtInStyles = styleResolver?.builtInStyles ?? EMPTY_BUILT_IN_STYLE_INDEX;
|
|
415
|
+
const noteReferences = noteReferenceLabels ? {
|
|
416
|
+
labelOf: noteReferenceLabels.labelOf,
|
|
417
|
+
numberBlock: () => {}
|
|
418
|
+
} : createNoteReferenceLabeler();
|
|
341
419
|
const draftBlocks = [];
|
|
342
420
|
const hashCounts = /* @__PURE__ */ new Map();
|
|
343
421
|
const usedBlockIds = /* @__PURE__ */ new Set();
|
|
344
422
|
const numberingReferenceKeys = /* @__PURE__ */ new Set();
|
|
423
|
+
const nextListLabel = createListLabelCounter();
|
|
345
424
|
const tables = [];
|
|
346
425
|
const tableIndexByStart = /* @__PURE__ */ new Map();
|
|
347
426
|
const path = [];
|
|
@@ -352,6 +431,44 @@ const createFolioAIEditSnapshotInternal = (doc, styleResolver) => {
|
|
|
352
431
|
if (!node.isTextblock) {
|
|
353
432
|
const disposition = classifyNonTextblockStoryTableNode(node);
|
|
354
433
|
if (disposition === STORY_TABLE_HIDDEN_SUBTREE) return false;
|
|
434
|
+
const preserved = node.attrs["_preserved"];
|
|
435
|
+
const preservedChildren = preserved !== null && typeof preserved === "object" && "children" in preserved ? preserved.children : void 0;
|
|
436
|
+
if (Array.isArray(preservedChildren)) for (const [carrierIndex, child] of preservedChildren.entries()) {
|
|
437
|
+
if (child === null || typeof child !== "object" || !("xml" in child)) continue;
|
|
438
|
+
const xml = child.xml;
|
|
439
|
+
if (typeof xml !== "string") continue;
|
|
440
|
+
const diagnostic = getOpaqueCarrierDiagnostic(xml);
|
|
441
|
+
if (diagnostic === void 0) continue;
|
|
442
|
+
draftBlocks.push({ block: {
|
|
443
|
+
id: `opaque-${String(pos)}-${String(carrierIndex)}`,
|
|
444
|
+
kind: "diagnostic",
|
|
445
|
+
text: diagnostic.text,
|
|
446
|
+
diagnostic: {
|
|
447
|
+
type: "opaqueCarrier",
|
|
448
|
+
carrier: diagnostic.carrier
|
|
449
|
+
}
|
|
450
|
+
} });
|
|
451
|
+
}
|
|
452
|
+
if (node.type.name === "preservedBlock") {
|
|
453
|
+
const xml = node.attrs["xml"];
|
|
454
|
+
if (typeof xml === "string") {
|
|
455
|
+
const readerText = node.attrs["readerText"];
|
|
456
|
+
const diagnostic = getOpaqueCarrierDiagnostic(xml, typeof readerText === "string" ? readerText : void 0);
|
|
457
|
+
if (diagnostic !== void 0) {
|
|
458
|
+
const id = `opaque-${String(pos)}`;
|
|
459
|
+
draftBlocks.push({ block: {
|
|
460
|
+
id,
|
|
461
|
+
kind: "diagnostic",
|
|
462
|
+
text: diagnostic.text,
|
|
463
|
+
diagnostic: {
|
|
464
|
+
type: "opaqueCarrier",
|
|
465
|
+
carrier: diagnostic.carrier
|
|
466
|
+
}
|
|
467
|
+
} });
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
return false;
|
|
471
|
+
}
|
|
355
472
|
if (disposition === STORY_TABLE) {
|
|
356
473
|
const tableIndex = tables.length;
|
|
357
474
|
tables.push({
|
|
@@ -369,7 +486,11 @@ const createFolioAIEditSnapshotInternal = (doc, styleResolver) => {
|
|
|
369
486
|
});
|
|
370
487
|
return true;
|
|
371
488
|
}
|
|
372
|
-
|
|
489
|
+
noteReferences.numberBlock(node);
|
|
490
|
+
const cleanBlock = buildCleanBlockText(node, pos, {
|
|
491
|
+
fieldResults: "text",
|
|
492
|
+
noteReferences
|
|
493
|
+
});
|
|
373
494
|
const { text } = cleanBlock;
|
|
374
495
|
const structuralBoundaries = projectFolioAIBlockStructuralBoundaries(cleanBlock);
|
|
375
496
|
const structuralBoundaryHash = hashFolioAIBlockStructuralBoundaryProjection(structuralBoundaries);
|
|
@@ -398,9 +519,11 @@ const createFolioAIEditSnapshotInternal = (doc, styleResolver) => {
|
|
|
398
519
|
}
|
|
399
520
|
usedBlockIds.add(id);
|
|
400
521
|
const headingLevel = getHeadingLevel(node, builtInStyles);
|
|
401
|
-
const
|
|
402
|
-
const
|
|
522
|
+
const listLabel = node.type.name === "paragraph" ? nextListLabel(expectParagraphAttrs(node)) : void 0;
|
|
523
|
+
const kind = getBlockKind(headingLevel, listLabel);
|
|
524
|
+
const displayLabel = getDisplayLabel(node, listLabel, kind === "heading");
|
|
403
525
|
const styleId = getStyleId(node);
|
|
526
|
+
const directOutlineLevel = node.type.name === "paragraph" ? expectParagraphAttrs(node)._originalFormatting?.outlineLevel : void 0;
|
|
404
527
|
const listLevel = getListLevel(node);
|
|
405
528
|
const listReference = getListReference(node);
|
|
406
529
|
const numberingReferenceKey = getNumberingReferenceKey(node);
|
|
@@ -428,6 +551,7 @@ const createFolioAIEditSnapshotInternal = (doc, styleResolver) => {
|
|
|
428
551
|
...headingLevel !== void 0 && { headingLevel },
|
|
429
552
|
...displayLabel !== void 0 && { displayLabel },
|
|
430
553
|
...styleId !== void 0 && { styleId },
|
|
554
|
+
...directOutlineLevel !== void 0 && { directOutlineLevel },
|
|
431
555
|
...listLevel !== void 0 && { listLevel },
|
|
432
556
|
...listReference !== void 0 && { listReference },
|
|
433
557
|
...directAlignment !== void 0 && { directAlignment },
|
|
@@ -453,6 +577,7 @@ const createFolioAIEditSnapshotInternal = (doc, styleResolver) => {
|
|
|
453
577
|
const anchors = {};
|
|
454
578
|
for (const draft of draftBlocks) {
|
|
455
579
|
blocks.push(draft.block);
|
|
580
|
+
if (draft.anchor === void 0) continue;
|
|
456
581
|
anchors[draft.block.id] = {
|
|
457
582
|
...draft.anchor,
|
|
458
583
|
hashOccurrenceCount: hashCounts.get(draft.anchor.textHash) ?? 0
|
|
@@ -470,15 +595,37 @@ const createFolioAIEditSnapshotInternal = (doc, styleResolver) => {
|
|
|
470
595
|
});
|
|
471
596
|
return snapshot;
|
|
472
597
|
};
|
|
598
|
+
const getOpaqueCarrierDiagnostic = (xml, readerText) => {
|
|
599
|
+
if (isAltChunkMarkup(xml)) return {
|
|
600
|
+
carrier: "w:altChunk",
|
|
601
|
+
text: readerText === void 0 ? "[Unsupported w:altChunk content]" : `[Unsupported w:altChunk content] ${readerText}`
|
|
602
|
+
};
|
|
603
|
+
const elementName = opaqueRevisionCarrierName(xml);
|
|
604
|
+
if (elementName !== void 0) return {
|
|
605
|
+
carrier: elementName,
|
|
606
|
+
text: OPAQUE_REVISION_CARRIER_READER_DIAGNOSTIC
|
|
607
|
+
};
|
|
608
|
+
return isOpaqueNestedRowMarkup(xml) ? {
|
|
609
|
+
carrier: "w:tr",
|
|
610
|
+
text: "[Unsupported nested w:tr content]"
|
|
611
|
+
} : void 0;
|
|
612
|
+
};
|
|
473
613
|
const createFolioAIEditSnapshot = (doc) => createFolioAIEditSnapshotInternal(doc, null);
|
|
474
614
|
/** @internal Use for an EditorState that owns the document's style resolver. */
|
|
475
615
|
const createFolioAIEditSnapshotWithStyleResolver = (doc, styleResolver) => createFolioAIEditSnapshotInternal(doc, styleResolver);
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
616
|
+
/**
|
|
617
|
+
* What a reader sees the block as, heading first: a numbered heading (`1.
|
|
618
|
+
* Scope`, numbered through its style or its own `w:numPr`) is a heading that
|
|
619
|
+
* shows a number, with the number in `displayLabel` and its level in
|
|
620
|
+
* `listLevel`. A paragraph is a list item when it shows a marker, which is
|
|
621
|
+
* when it has a label. One whose numbering shows none (a `w:vanish` level, a
|
|
622
|
+
* level its list does not define, or `w:numId="0"` cancelling its style's
|
|
623
|
+
* numbering) reads as prose and is a paragraph, keeping its `listLevel` and
|
|
624
|
+
* `listReference`.
|
|
625
|
+
*/
|
|
626
|
+
const getBlockKind = (headingLevel, listLabel) => {
|
|
480
627
|
if (headingLevel !== void 0) return "heading";
|
|
481
|
-
return "paragraph";
|
|
628
|
+
return listLabel === void 0 ? "paragraph" : "listItem";
|
|
482
629
|
};
|
|
483
630
|
/** The block's 1-based heading level, as {@link resolveHeadingLevel} classifies it. */
|
|
484
631
|
const getHeadingLevel = (node, styles) => {
|
|
@@ -489,9 +636,8 @@ const getHeadingLevel = (node, styles) => {
|
|
|
489
636
|
}, styles);
|
|
490
637
|
return level === void 0 ? void 0 : level + 1;
|
|
491
638
|
};
|
|
492
|
-
const getDisplayLabel = (node, isHeading) => {
|
|
493
|
-
|
|
494
|
-
if (typeof listMarker === "string" && listMarker.trim().length > 0) return listMarker.trim();
|
|
639
|
+
const getDisplayLabel = (node, listLabel, isHeading) => {
|
|
640
|
+
if (listLabel !== void 0) return listLabel;
|
|
495
641
|
const styleId = node.attrs["styleId"];
|
|
496
642
|
if (isHeading && typeof styleId === "string") return styleId;
|
|
497
643
|
};
|
|
@@ -541,16 +687,19 @@ const RUN_FORMATTING_OVERRIDE_MARK = "runFormattingOverride";
|
|
|
541
687
|
const CHARACTER_STYLE_MARK = "characterStyle";
|
|
542
688
|
const getPreviewRuns = ({ node, nodeFrom, cleanBlock, styleResolver }) => {
|
|
543
689
|
const runs = [];
|
|
544
|
-
const defaultStyle = getDefaultPreviewRunStyle(node);
|
|
690
|
+
const defaultStyle = getDefaultPreviewRunStyle(node, styleResolver);
|
|
545
691
|
let paragraphStyleContext;
|
|
546
692
|
let cleanOffset = 0;
|
|
547
693
|
node.descendants((child, relativePosition) => {
|
|
548
|
-
const
|
|
694
|
+
const start = nodeFrom + 1 + relativePosition;
|
|
695
|
+
const noteReference = cleanBlock.structuralBoundaries.find((boundary) => boundary.type === "noteReference" && boundary.from === start);
|
|
696
|
+
let text;
|
|
697
|
+
if (noteReference !== void 0) text = cleanBlock.text.slice(noteReference.offset, noteReference.offset + noteReference.length);
|
|
698
|
+
else text = child.isText ? child.text : runFormattingInlineAtomCleanText(child);
|
|
549
699
|
if (text === void 0 || text === null || text.length === 0) return true;
|
|
550
700
|
if (child.marks.some((mark) => mark.type.name === DELETION_MARK || mark.type.name === HIDDEN_MARK)) return false;
|
|
551
|
-
const start = nodeFrom + 1 + relativePosition;
|
|
552
701
|
while ((cleanBlock.offsets[cleanOffset] ?? Number.POSITIVE_INFINITY) < start) cleanOffset++;
|
|
553
|
-
const lastCharacterPosition = child.isText ? start + text.length - 1 : start;
|
|
702
|
+
const lastCharacterPosition = child.isText && noteReference === void 0 ? start + text.length - 1 : start;
|
|
554
703
|
if (cleanBlock.offsets[cleanOffset] !== start || cleanBlock.offsets[cleanOffset + text.length - 1] !== lastCharacterPosition) return false;
|
|
555
704
|
cleanOffset += text.length;
|
|
556
705
|
const style = getPreviewRunStyle(child.marks, defaultStyle);
|
|
@@ -583,8 +732,13 @@ const getPreviewRuns = ({ node, nodeFrom, cleanBlock, styleResolver }) => {
|
|
|
583
732
|
if (runs.every(isUnstyledPreviewRun)) return;
|
|
584
733
|
return runs;
|
|
585
734
|
};
|
|
586
|
-
const getDefaultPreviewRunStyle = (node) => {
|
|
587
|
-
const
|
|
735
|
+
const getDefaultPreviewRunStyle = (node, styleResolver) => {
|
|
736
|
+
const attrs = expectParagraphAttrs(node);
|
|
737
|
+
const formatting = attrs.defaultTextFormatting ?? resolveParagraphBodyRunFormatting({
|
|
738
|
+
styleId: attrs.styleId,
|
|
739
|
+
tableRunFormatting: attrs._tableRunFormatting,
|
|
740
|
+
styleResolver
|
|
741
|
+
}).defaultFormatting;
|
|
588
742
|
if (typeof formatting !== "object" || formatting === null) return {};
|
|
589
743
|
return {
|
|
590
744
|
...getBooleanTextFormatting(formatting),
|
|
@@ -717,4 +871,4 @@ const isEmptyPreviewRunStyle = ({ bold, italic, underline, strike, fontFamily, f
|
|
|
717
871
|
const sameDirectFormatting = (left, right) => left === void 0 && isEmptyPreviewRunStyle(right) || left !== void 0 && left.bold === right.bold && left.italic === right.italic && left.underline === right.underline && left.strike === right.strike && left.fontFamily === right.fontFamily && left.fontSizePt === right.fontSizePt && left.color === right.color;
|
|
718
872
|
const isUnstyledPreviewRun = ({ directFormatting, ...style }) => isEmptyPreviewRunStyle(style) && (directFormatting === void 0 || isEmptyPreviewRunStyle(directFormatting));
|
|
719
873
|
//#endregion
|
|
720
|
-
export { createFolioAIEditSnapshot, createFolioAIEditSnapshotWithStyleResolver, createFolioAITextRangeHandle, detachFolioAIEditSnapshotExternalHyperlinks, folioStoryTables, hashFolioAIBlockStructuralBoundaries, hashFolioAIBlockText, isFolioAIContentBlock, isHiddenTableRow, normalizeFolioAIBlockText, numberingReferenceKeysOf, projectFolioAIBlockStructuralBoundaries, remapFolioAIEditSnapshotNumberingReferences, remapFolioAIEditSnapshotStyleReferences, sourceDocumentOf, storyTablesOf, styleResolverOf, trailingBodyBlockId };
|
|
874
|
+
export { alignedNoteReferenceLabels, collectNoteReferenceLabels, createFolioAIEditSnapshot, createFolioAIEditSnapshotWithStyleResolver, createFolioAITextRangeHandle, detachFolioAIEditSnapshotExternalHyperlinks, folioStoryTables, hashFolioAIBlockStructuralBoundaries, hashFolioAIBlockText, isFolioAIContentBlock, isHiddenTableRow, normalizeFolioAIBlockText, numberingReferenceKeysOf, projectFolioAIBlockStructuralBoundaries, relabelFolioAIEditSnapshotNoteReferences, remapFolioAIEditSnapshotNumberingReferences, remapFolioAIEditSnapshotStyleReferences, sourceDocumentOf, storyTablesOf, styleResolverOf, trailingBodyBlockId };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { TableRectangle } from "./table-mutation-plan.js";
|
|
2
|
-
import { Transaction } from "prosemirror-state";
|
|
3
2
|
import { Node } from "prosemirror-model";
|
|
3
|
+
import { Transaction } from "prosemirror-state";
|
|
4
4
|
//#region src/ai-edits/table-cell-mutations.d.ts
|
|
5
5
|
type TableCellRectangleMutationOptions = {
|
|
6
6
|
tr: Transaction;
|
|
@@ -3,6 +3,7 @@ import { expectTableCellAttrs } from "../prosemirror/attrs/index.js";
|
|
|
3
3
|
import { standaloneTableCellFromProseMirror } from "../prosemirror/conversion/fromProseDoc.js";
|
|
4
4
|
import { standaloneTableCellToProseMirror } from "../prosemirror/conversion/toProseDoc.js";
|
|
5
5
|
import { markStructuralChange } from "../prosemirror/extensions/features/ParagraphChangeTrackerExtension.js";
|
|
6
|
+
import { removeRowsWithoutCells } from "../prosemirror/tableGridMutation.js";
|
|
6
7
|
import { tableRectanglesEqual } from "./table-mutation-plan.js";
|
|
7
8
|
import { tableRectangleCutsMergedCell } from "./table-targets.js";
|
|
8
9
|
import { Result } from "better-result";
|
|
@@ -62,6 +63,7 @@ const mergeTableRectangle = ({ tr, tablePosition, table, rectangle }) => {
|
|
|
62
63
|
const contentStart = isEmptyTableCell(merged.cell) ? absoluteMergedPosition + 1 : contentEnd;
|
|
63
64
|
tr = tr.replaceWith(contentStart, contentEnd, appendedContent);
|
|
64
65
|
}
|
|
66
|
+
removeRowsWithoutCells(tr, tablePosition);
|
|
65
67
|
return tr;
|
|
66
68
|
};
|
|
67
69
|
const mergeTopRowColwidths = (cells, mergedColspan) => {
|
|
@@ -121,7 +123,8 @@ const mergeTrackedVerticalTableCells = ({ tr, tablePosition, table, rectangle, r
|
|
|
121
123
|
});
|
|
122
124
|
}
|
|
123
125
|
const origin = cells.at(0);
|
|
124
|
-
|
|
126
|
+
const leavesRowWithoutCells = cells.slice(1).some(({ position }) => table.child(map.findCell(position).top).childCount === 1);
|
|
127
|
+
if (!origin || leavesRowWithoutCells || cells.length !== rectangle.bottom - rectangle.top || cells.some(({ cell }) => cell.type !== origin.cell.type) || cells.slice(1).some(({ cell }) => !isEmptyTableCell(cell))) return null;
|
|
125
128
|
const continuationCells = [];
|
|
126
129
|
for (const { cell } of cells.slice(1)) {
|
|
127
130
|
const continuation = standaloneTableCellFromProseMirror(cell);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { FolioStoryTable } from "./snapshot.js";
|
|
2
|
-
import { Transaction } from "prosemirror-state";
|
|
3
2
|
import { Node } from "prosemirror-model";
|
|
3
|
+
import { Transaction } from "prosemirror-state";
|
|
4
4
|
//#region src/ai-edits/table-geometry.d.ts
|
|
5
5
|
/** Where a cell sits: which table, which row of it, which cell of that row. */
|
|
6
6
|
type TableCellCoordinate = {
|