@a3s-lab/office 0.48.0 → 0.49.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.
@@ -1,7 +1,7 @@
1
1
  import jszip from "jszip";
2
2
  import { documentRunBorderDomAttributes, contentTypeForPart, bytesToDataUrl, OoxmlPackage, normalizeDocumentIndexesHtml, firstDescendant, normalizeDocumentParagraphId, applyDocumentParagraphIdentityToElement, descendants, documentHighlightDomAttributes, attribute as work_ooxml_package_attribute, documentHighlightFromDocxValue, serializeDocxThemeReference, normalizeDocumentTableOfContentsHtml, directChildren, documentParagraphShadingDomAttributes, documentProofingDomAttributes, documentParagraphBordersDomAttributes, directChild, documentScriptFontSegments, normalizeDocumentParagraphIdentity, documentRunShadingDomAttributes } from "./6968.js";
3
3
  import { normalizeDocumentBookmarkName, renderDocumentTableCellMarginOverrides, applyDocumentImageIdentityToElement, documentCharacterPositionDomAttributes, createDocumentImageIdentityRegistry, normalizeDocumentColumns, documentStrikeDomAttributes, normalizeDocumentImageWrapContour, DOCUMENT_TABLE_ROW_ID_ATTRIBUTE, normalizeDocumentBookmarksHtml, applyDocumentTableRowIdentityToElement, normalizeDocumentCaptionsHtml, documentCharacterSpacingDomAttributes, normalizeDocumentImageAlignment, applyDocumentImageLayerToElement, normalizeDocumentNotesHtml, normalizeDocumentBookmarkReferencesHtml, documentCitationTagsFromInstruction, applyDocumentImageWrapContourToElement, documentUnderlineDomAttributes, documentHiddenTextDomAttributes, normalizeDocumentPageChrome, normalizeDocumentImageLayoutOptions, applyDocumentImageCropToElement, documentPageMarginsForLayout, applyDocumentPageGeometry, createDocumentNoteElement, normalizeDocumentImageLayer, DOCUMENT_TABLE_ROW_TEXT_ID_ATTRIBUTE, documentBookmarkReferenceInstruction, applyDocumentImageTransformToElement, documentInitialSectionLayout, normalizeDocumentFieldsHtml, documentCaptionLabel, documentTableBordersFromElement, isContourImageLayout, normalizeDocumentCitationsHtml, normalizeDocumentImageIdentity, normalizeDocumentImageWrapSide, supportedDocxDocumentFieldInstruction, documentKerningDomAttributes, docxDocumentFieldKind, renderDocumentTableBorders, docxBookmarkReferenceTarget, normalizeDocumentImagePosition, wrapsBesideImage, documentPageChromeLegacyFields, documentCharacterScaleDomAttributes, DEFAULT_DOCUMENT_TABLE_GEOMETRY, documentSectionDomAttributes, DEFAULT_DOCUMENT_TABLE_CELL_MARGINS, documentPageMarginBody, documentLegacyTextEffectsDomAttributes, normalizeDocumentTableRowIdentity, normalizeDocumentBookmarkNativeId, documentOpenTypeDomAttributes, uniqueDocumentImageIdentity, applyDocumentTableGeometryToElement, normalizeDocumentImageCrop, documentContentLayoutProperties, documentEmphasisMarkDomAttributes, docxDocumentFieldTarget } from "./4174.js";
4
- import { hasImportedDocxIndexMarkers, markDocxRunFormattingIntoState, applyImportedDocxParagraphSpacingMarkers, hasImportedDocxParagraphDirectionMarkers, hasImportedDocxParagraphIndentMarkers, resolveDocxParagraphBordersForParagraph, applyImportedDocxTextBoxMarkers, applyImportedDocxNumberingChangeMarkers, applyImportedDocxParagraphShadingMarkers, markDocxParagraphSpacing, docxCaptionBookmark, docxFieldResultText, applyImportedDocxContentControlMarkers, createDocxParagraphStyleResolver, markDocxTextBoxes, docxTableCellStyleLayers, createImportedDocxRunFormattingMarkerState, markDocxContentControls, parseDocxPaperSource, resolveDocxLegacyTextEffects, markDocxTablesOfContents, applyImportedDocxIndexMarkers, docxTablePropertySources, applyImportedDocxTableOfContentsMarkers, markDocxIndexes, parseDirectDocxParagraphShading, markDocxPackageEquations, hasImportedDocxParagraphSpacingMarkers, markDocxParagraphIndents, parseDocxPageMargins, resolveDocxRunBorder, parseDocxPageGeometry, docxCaptionReferenceTarget, applyImportedDocxEquationMarkers, hasImportedDocxParagraphPaginationMarkers, createDocxTableStyleResolver, hasImportedDocxParagraphShadingMarkers, isDocxEquationLikeRoot, importedDocxStrike, docxTableRunPropertySources, hasImportedDocxNumberingChangeMarkers, inspectDocxPageMarginSettings, hasImportedDocxRunFormattingMarkers, hasImportedDocxTableOfContentsMarkers, applyImportedDocxParagraphFormattingChangeMarkers, hasImportedDocxContentControlMarkers, hasImportedDocxParagraphTabStopMarkers, resolveDocxRunShading, docxCaptionSequenceKind, applyImportedDocxRunFormattingMarkers, markDocxParagraphAlignments, markDocxParagraphDirections, hasImportedDocxParagraphBorderMarkers, hasImportedDocxParagraphFormattingChangeMarkers, resolveDocxHiddenText, docxEquationHtml, docxFieldOccurrences, hasImportedDocxParagraphAlignmentMarkers, markDocxParagraphTabStops, applyImportedDocxParagraphIndentMarkers, resolveDocxTableStyleResolver, applyImportedDocxParagraphAlignmentMarkers, hasImportedDocxTextBoxMarkers, markDocxNumberingChanges, importedDocxUnderline, markDocxParagraphBorders, applyImportedDocxParagraphDirectionMarkers, markDocxParagraphPagination, docxFieldOccurrenceIsInlineEditable, applyImportedDocxParagraphBorderMarkers, applyImportedDocxParagraphPaginationMarkers, applyImportedDocxParagraphTabStopMarkers, docxRunPropertySources, markDocxParagraphFormattingChanges, markDocxParagraphShading, parseDocxPageBorders } from "./0~3320.js";
4
+ import { hasImportedDocxIndexMarkers, markDocxRunFormattingIntoState, applyImportedDocxParagraphSpacingMarkers, hasImportedDocxParagraphDirectionMarkers, hasImportedDocxParagraphIndentMarkers, resolveDocxParagraphBordersForParagraph, applyImportedDocxTextBoxMarkers, applyImportedDocxNumberingChangeMarkers, applyImportedDocxParagraphShadingMarkers, applyImportedDocxChangeMarkers, markDocxParagraphSpacing, docxCaptionBookmark, docxFieldResultText, applyImportedDocxContentControlMarkers, createDocxParagraphStyleResolver, markDocxTextBoxes, docxTableCellStyleLayers, markDocxTextChanges, markDocxContentControls, createImportedDocxRunFormattingMarkerState, parseDocxPaperSource, resolveDocxLegacyTextEffects, markDocxTablesOfContents, applyImportedDocxIndexMarkers, docxTablePropertySources, applyImportedDocxTableOfContentsMarkers, markDocxIndexes, parseDirectDocxParagraphShading, markDocxPackageEquations, hasImportedDocxParagraphSpacingMarkers, markDocxParagraphIndents, parseDocxPageMargins, resolveDocxRunBorder, parseDocxPageGeometry, docxCaptionReferenceTarget, applyImportedDocxEquationMarkers, hasImportedDocxParagraphPaginationMarkers, createDocxTableStyleResolver, hasImportedDocxParagraphShadingMarkers, isDocxEquationLikeRoot, importedDocxStrike, docxTableRunPropertySources, hasImportedDocxNumberingChangeMarkers, inspectDocxPageMarginSettings, hasImportedDocxRunFormattingMarkers, hasImportedDocxTableOfContentsMarkers, applyImportedDocxParagraphFormattingChangeMarkers, hasImportedDocxContentControlMarkers, hasImportedDocxParagraphTabStopMarkers, resolveDocxRunShading, docxCaptionSequenceKind, applyImportedDocxRunFormattingMarkers, markDocxParagraphAlignments, markDocxParagraphDirections, hasImportedDocxParagraphBorderMarkers, hasImportedDocxParagraphFormattingChangeMarkers, resolveDocxHiddenText, docxEquationHtml, docxFieldOccurrences, hasImportedDocxParagraphAlignmentMarkers, markDocxParagraphTabStops, applyImportedDocxParagraphIndentMarkers, applyImportedDocxParagraphDirectionMarkers, applyImportedDocxParagraphAlignmentMarkers, resolveDocxTableStyleResolver, hasImportedDocxTextBoxMarkers, importedDocxUnderline, markDocxParagraphBorders, markDocxNumberingChanges, markDocxParagraphPagination, hasImportedDocxChangeMarkers, docxFieldOccurrenceIsInlineEditable, applyImportedDocxParagraphBorderMarkers, applyImportedDocxParagraphPaginationMarkers, applyImportedDocxParagraphTabStopMarkers, docxRunPropertySources, markDocxParagraphFormattingChanges, markDocxParagraphShading, parseDocxPageBorders } from "./0~5600.js";
5
5
  import { readDocxBibliography, resolveDocxProofing, XMLNS_NAMESPACE, docxCharacterScalePercentFromProperties, docxCharacterSpacingTwipsFromProperties, parseBoundedDocxInteger, resolveDocxOpenTypeFeatures, xmlAttributeLocalName, resolveDocxEmphasisMark, createDocxThemeResolver, docxCharacterPositionHalfPointsFromProperties, readDocxImageTransform, docxThemeColor, parseDocxParagraphDefaultCollapsed, resolveDocxKerningThresholdHalfPoints, resolveDocxThemeResolver, DOCX_WORDPROCESSING_NAMESPACES, xmlAttributeNamespace } from "./0~3534.js";
6
6
  import { importDocxPageColor, importedDocumentNoteId } from "./0~4980.js";
7
7
  const WORD_NAMESPACE = 'http://schemas.openxmlformats.org/wordprocessingml/2006/main';
@@ -428,121 +428,6 @@ function work_docx_caption_import_textNodes(root) {
428
428
  while(walker.nextNode())nodes.push(walker.currentNode);
429
429
  return nodes;
430
430
  }
431
- const work_docx_change_import_WORD_NAMESPACE = 'http://schemas.openxmlformats.org/wordprocessingml/2006/main';
432
- const work_docx_change_import_XML_NAMESPACE = 'http://www.w3.org/XML/1998/namespace';
433
- function markDocxTextChanges(document) {
434
- const changes = [];
435
- const revisions = [
436
- ...descendants(document, 'ins'),
437
- ...descendants(document, 'del')
438
- ].sort(compareDocumentOrder);
439
- for (const revision of revisions){
440
- if (!revision.parentNode || closestRevision(revision.parentElement)) continue;
441
- const kind = 'del' === revision.localName ? 'deletion' : 'insertion';
442
- if (!revisionText(revision, kind)) continue;
443
- if ('deletion' === kind) convertDeletedText(document, revision);
444
- const index = changes.length + 1;
445
- const sourceId = work_ooxml_package_attribute(revision, 'id')?.trim() ?? '';
446
- const id = uniqueChangeId(sourceId ? `docx-change-${sourceId}` : `docx-change-${index}`, changes);
447
- const marker = {
448
- id,
449
- kind,
450
- author: work_ooxml_package_attribute(revision, 'author')?.trim() || '未知审阅者',
451
- date: normalizeDate(work_ooxml_package_attribute(revision, 'date')),
452
- start: `__A3S_WORK_CHANGE_START_${index}__`,
453
- end: `__A3S_WORK_CHANGE_END_${index}__`
454
- };
455
- unwrapRevision(document, revision, marker.start, marker.end);
456
- changes.push(marker);
457
- }
458
- return {
459
- changes
460
- };
461
- }
462
- function applyImportedDocxChangeMarkers(document, markers) {
463
- for (const marker of markers.changes){
464
- const element = document.createElement('deletion' === marker.kind ? 'del' : 'ins');
465
- element.dataset.documentChange = 'true';
466
- element.dataset.changeKind = marker.kind;
467
- element.dataset.changeId = marker.id;
468
- element.dataset.changeAuthor = marker.author;
469
- element.dataset.changeDate = marker.date;
470
- wrapMarkerRange(document.body, marker.start, marker.end, element);
471
- }
472
- }
473
- function hasImportedDocxChangeMarkers(markers) {
474
- return markers.changes.length > 0;
475
- }
476
- function revisionText(revision, kind) {
477
- const names = 'deletion' === kind ? [
478
- 'delText',
479
- 't'
480
- ] : [
481
- 't'
482
- ];
483
- return names.flatMap((name)=>descendants(revision, name)).map((element)=>element.textContent ?? '').join('');
484
- }
485
- function convertDeletedText(document, revision) {
486
- for (const deleted of descendants(revision, 'delText')){
487
- const text = document.createElementNS(work_docx_change_import_WORD_NAMESPACE, 'w:t');
488
- const preserve = deleted.getAttributeNS(work_docx_change_import_XML_NAMESPACE, 'space') ?? deleted.getAttribute('xml:space');
489
- if (preserve) text.setAttributeNS(work_docx_change_import_XML_NAMESPACE, 'xml:space', preserve);
490
- text.textContent = deleted.textContent;
491
- deleted.replaceWith(text);
492
- }
493
- }
494
- function unwrapRevision(document, revision, start, end) {
495
- const parent = revision.parentNode;
496
- if (!parent) return;
497
- parent.insertBefore(work_docx_change_import_markerRun(document, start), revision);
498
- while(revision.firstChild)parent.insertBefore(revision.firstChild, revision);
499
- parent.insertBefore(work_docx_change_import_markerRun(document, end), revision);
500
- revision.remove();
501
- }
502
- function work_docx_change_import_markerRun(document, value) {
503
- const run = document.createElementNS(work_docx_change_import_WORD_NAMESPACE, 'w:r');
504
- const text = document.createElementNS(work_docx_change_import_WORD_NAMESPACE, 'w:t');
505
- text.setAttributeNS(work_docx_change_import_XML_NAMESPACE, 'xml:space', 'preserve');
506
- text.textContent = value;
507
- run.append(text);
508
- return run;
509
- }
510
- function wrapMarkerRange(root, start, end, wrapper) {
511
- const html = root.innerHTML;
512
- const startIndex = html.indexOf(start);
513
- const endIndex = html.indexOf(end, startIndex + start.length);
514
- if (startIndex < 0 || endIndex < 0) return false;
515
- const closingTag = `</${wrapper.localName}>`;
516
- const serializedWrapper = wrapper.outerHTML;
517
- if (!serializedWrapper.endsWith(closingTag)) return false;
518
- const openingTag = serializedWrapper.slice(0, -closingTag.length);
519
- root.innerHTML = `${html.slice(0, startIndex)}${openingTag}${html.slice(startIndex + start.length, endIndex)}${closingTag}${html.slice(endIndex + end.length)}`;
520
- return true;
521
- }
522
- function closestRevision(element) {
523
- let current = element;
524
- while(current){
525
- if ('ins' === current.localName || 'del' === current.localName) return current;
526
- current = current.parentElement;
527
- }
528
- return null;
529
- }
530
- function uniqueChangeId(base, changes) {
531
- const ids = new Set(changes.map((change)=>change.id));
532
- if (!ids.has(base)) return base;
533
- let suffix = 2;
534
- while(ids.has(`${base}-${suffix}`))suffix += 1;
535
- return `${base}-${suffix}`;
536
- }
537
- function normalizeDate(value) {
538
- if (!value) return '';
539
- const time = Date.parse(value);
540
- return Number.isFinite(time) ? new Date(time).toISOString() : '';
541
- }
542
- function compareDocumentOrder(left, right) {
543
- if (left === right) return 0;
544
- return left.compareDocumentPosition(right) & Node.DOCUMENT_POSITION_FOLLOWING ? -1 : 1;
545
- }
546
431
  const work_docx_citation_import_WORD_NAMESPACE = 'http://schemas.openxmlformats.org/wordprocessingml/2006/main';
547
432
  const work_docx_citation_import_XML_NAMESPACE = 'http://www.w3.org/XML/1998/namespace';
548
433
  function markDocxCitationFields(document) {
@@ -744,7 +629,7 @@ async function markDocxComments(document, archive) {
744
629
  const workIds = new Map();
745
630
  const ranges = [];
746
631
  const comments = [];
747
- const starts = descendants(document, 'commentRangeStart').sort(work_docx_comment_import_compareDocumentOrder);
632
+ const starts = descendants(document, 'commentRangeStart').sort(compareDocumentOrder);
748
633
  for (const start of starts){
749
634
  const sourceId = work_ooxml_package_attribute(start, 'id')?.trim() ?? '';
750
635
  if (!sourceId) continue;
@@ -794,7 +679,7 @@ async function readCommentDefinitions(archive) {
794
679
  return {
795
680
  sourceId: work_ooxml_package_attribute(comment, 'id')?.trim() ?? '',
796
681
  author: work_ooxml_package_attribute(comment, 'author')?.trim() || '未知审阅者',
797
- date: work_docx_comment_import_normalizeDate(work_ooxml_package_attribute(comment, 'date')),
682
+ date: normalizeDate(work_ooxml_package_attribute(comment, 'date')),
798
683
  text: commentText(comment),
799
684
  paraId,
800
685
  parentParaId: metadata?.parentParaId ?? '',
@@ -922,12 +807,12 @@ function uniqueWorkCommentId(base, comments) {
922
807
  while(ids.has(`${base}-${suffix}`))suffix += 1;
923
808
  return `${base}-${suffix}`;
924
809
  }
925
- function work_docx_comment_import_normalizeDate(value) {
810
+ function normalizeDate(value) {
926
811
  if (!value) return '';
927
812
  const time = Date.parse(value);
928
813
  return Number.isFinite(time) ? new Date(time).toISOString() : '';
929
814
  }
930
- function work_docx_comment_import_compareDocumentOrder(left, right) {
815
+ function compareDocumentOrder(left, right) {
931
816
  if (left === right) return 0;
932
817
  return left.compareDocumentPosition(right) & Node.DOCUMENT_POSITION_FOLLOWING ? -1 : 1;
933
818
  }
@@ -1,7 +1,7 @@
1
1
  import { __webpack_require__ } from "./0~rslib-runtime.js";
2
2
  import { contentTypeForPart, directChildren, OoxmlPackage, parseXml, firstDescendant, isDocumentParagraphArtBorderStyle, descendants, directChild, xmlContainsAnyElement, attribute, documentRunBorderIsVisible } from "./6968.js";
3
3
  import { normalizeDocumentBookmarkName, docxBookmarkReferenceTarget, DOCUMENT_LEGACY_TEXT_EFFECT_NAMES, normalizeDocumentBookmarkNativeId, supportedDocxDocumentFieldInstruction, supportedDocxBookmarkReferenceInstruction, documentCitationTagsFromInstruction, docxDocumentFieldKind, docxDocumentFieldTarget } from "./4174.js";
4
- import { isSupportedDocxEquationPlacement, inspectDocxPageSize, inspectDocxContentControls, hasInvalidDocxFieldStructure, createDocxTableStyleResolver, inspectDocxRunBorder, inspectDocxEquation, inspectDocxPageMarginSettings, inspectDocxRunShading, supportedDocxIndexField, docxCaptionSequenceKind, inspectDocxPageBorders, docxCaptionBookmark, inspectDocxLegacyTextEffects, inspectDocxPageMargins, inspectDocxPaperSource, createDocxParagraphStyleResolver, inspectDocxRunFonts, supportedDocxTableOfContentsField, docxFieldOccurrences, supportedDocxIndexEntryField, inspectDocxTextBoxes, markDocxParagraphBorders, isSupportedDocxParagraphFormattingChange, docxFieldOccurrenceIsInlineEditable, inspectDocxHiddenText, isSupportedDocxRunFormattingChange, isSupportedDocxNumberingChange, markDocxParagraphShading, docxFieldInstructions } from "./0~3320.js";
4
+ import { isSupportedDocxEquationPlacement, inspectDocxPageSize, inspectDocxContentControls, hasInvalidDocxFieldStructure, createDocxTableStyleResolver, inspectDocxRunBorder, inspectDocxEquation, inspectDocxPageMarginSettings, inspectDocxRunShading, supportedDocxMovePairCount, supportedDocxIndexField, docxCaptionSequenceKind, inspectDocxPageBorders, isSupportedDocxMoveChange, docxCaptionBookmark, inspectDocxLegacyTextEffects, inspectDocxPageMargins, inspectDocxPaperSource, createDocxParagraphStyleResolver, inspectDocxRunFonts, supportedDocxTableOfContentsField, docxFieldOccurrences, supportedDocxIndexEntryField, inspectDocxTextBoxes, markDocxParagraphBorders, isSupportedDocxParagraphFormattingChange, docxFieldOccurrenceIsInlineEditable, inspectDocxHiddenText, isSupportedDocxRunFormattingChange, isSupportedDocxNumberingChange, markDocxParagraphShading, docxFieldInstructions } from "./0~5600.js";
5
5
  import { normalizeDocumentHref } from "./0~work-document-links.js";
6
6
  import { xmlAttributeLocalName, resolveDocxOpenTypeFeatures, inspectDocxProofingLanguages, readDocxBibliography, inspectDocxEmphasisMark, createDocxThemeResolver, DOCX_WORD_2010_NAMESPACE, inspectDocxNoProof, parseDocxParagraphDefaultCollapsed, readDocxImageTransform, DOCX_WORDPROCESSING_NAMESPACES, xmlAttributeNamespace, inspectDocxKerningThresholdHalfPoints } from "./0~3534.js";
7
7
  import { updateSpreadsheetWorksheetCompatibilitySummary, inspectXlsxPivotTables, isSupportedXlsxChartLegend, isSupportedXlsxChartPlotLayout, xlsxChartNodeUsesStackedGrouping, isSupportedXlsxChartSeriesFormatting, readXlsxFormulaFeaturesFromPackage, inspectXlsxHeaderFooterText, emptySpreadsheetWorksheetCompatibilitySummary, isSupportedXlsxWorksheetImageContentType, MAX_XLSX_WORKSHEET_IMAGE_BYTES, xlsxChartSeriesFormattingShapeProperties, diagnoseXlsxProtection, isEditableXlsxPaperSizeCode, isSupportedXlsxCombinationChartNodes } from "./4166.js";
@@ -2223,6 +2223,14 @@ async function analyzeDocxCompatibility(file, messages, sourcePackage) {
2223
2223
  ...descendants(document, 'ins'),
2224
2224
  ...descendants(document, 'del')
2225
2225
  ];
2226
+ const moveFromRevisions = descendants(document, 'moveFrom');
2227
+ const moveToRevisions = descendants(document, 'moveTo');
2228
+ const moveRevisionCount = moveFromRevisions.length + moveToRevisions.length;
2229
+ const supportedMovePairCount = supportedDocxMovePairCount(document);
2230
+ const supportedMoveRevisionCount = [
2231
+ ...moveFromRevisions,
2232
+ ...moveToRevisions
2233
+ ].filter(isSupportedDocxMoveChange).length;
2226
2234
  const runFormattingRevisions = descendants(document, 'rPrChange');
2227
2235
  const paragraphFormattingRevisions = descendants(document, 'pPrChange');
2228
2236
  const numberingRevisions = descendants(document, 'numberingChange');
@@ -2233,14 +2241,17 @@ async function analyzeDocxCompatibility(file, messages, sourcePackage) {
2233
2241
  if (supportedRunFormattingRevisionCount) issues.push(work_office_diagnostics_issue('docx.revisions.formatting', 'Character-formatting revisions', `${supportedRunFormattingRevisionCount} bounded character-formatting revision(s) preserve author, date, current formatting, and prior bold, italic, underline, strike, text case, hidden text, outline, shadow, emboss, imprint, subscript, superscript, font, size, color, highlight, character borders, character shading, character scale, spacing, kerning threshold, emphasis mark, baseline position, grid state, proofing languages, and explicit proofing state. They remain reviewable in Work and round-trip as native w:rPrChange records.`, 'info'));
2234
2242
  if (supportedParagraphFormattingRevisionCount) issues.push(work_office_diagnostics_issue('docx.revisions.paragraph-formatting', 'Paragraph-formatting revisions', `${supportedParagraphFormattingRevisionCount} bounded paragraph-formatting revision(s) preserve author, date, current formatting, and prior alignment, direction, indentation, spacing, pagination, outline, tab-stop, border, shading, and collapsed state. They remain reviewable in Work and round-trip as native w:pPrChange records.`, 'info'));
2235
2243
  if (supportedNumberingRevisionCount) issues.push(work_office_diagnostics_issue('docx.revisions.numbering', 'Numbering revisions', `${supportedNumberingRevisionCount} bounded ordered-list numbering revision(s) preserve author, date, prior start, and common decimal, letter, or Roman formats. Contiguous list-item records remain reviewable as one Work change and round-trip as native w:numberingChange records.`, 'info'));
2236
- if (textRevisions.some((revision)=>!descendants(revision, 't').length && !descendants(revision, 'delText').length) || supportedRunFormattingRevisionCount !== runFormattingRevisions.length || supportedParagraphFormattingRevisionCount !== paragraphFormattingRevisions.length || supportedNumberingRevisionCount !== numberingRevisions.length || [
2237
- 'moveFrom',
2238
- 'moveTo',
2244
+ if (supportedMovePairCount) issues.push(work_office_diagnostics_issue('docx.revisions.move', 'Move revisions', `${supportedMovePairCount} bounded text move revision(s) preserve author, date, source and destination text, remain reviewable as one atomic Work change, and round-trip as native w:moveFrom and w:moveTo records. Rich content, range markers, and relationship-bound moves remain on the compatibility path.`, 'info'));
2245
+ if (textRevisions.some((revision)=>!descendants(revision, 't').length && !descendants(revision, 'delText').length) || supportedRunFormattingRevisionCount !== runFormattingRevisions.length || supportedParagraphFormattingRevisionCount !== paragraphFormattingRevisions.length || supportedNumberingRevisionCount !== numberingRevisions.length || supportedMoveRevisionCount !== moveRevisionCount || supportedMoveRevisionCount !== 2 * supportedMovePairCount || [
2246
+ 'moveFromRangeStart',
2247
+ 'moveFromRangeEnd',
2248
+ 'moveToRangeStart',
2249
+ 'moveToRangeEnd',
2239
2250
  'tblPrChange',
2240
2251
  'trPrChange',
2241
2252
  'tcPrChange',
2242
2253
  'sectPrChange'
2243
- ].some((name)=>descendants(document, name).length)) issues.push(work_office_diagnostics_issue('docx.revisions.structural', 'Structural revisions', 'Moved content plus unsupported character formatting, paragraph formatting, numbering, section, row, cell, and table-property revisions may be normalized; Work currently reviews body-text insertions/deletions and bounded character-, paragraph-, and ordered-list-numbering subsets.'));
2254
+ ].some((name)=>descendants(document, name).length)) issues.push(work_office_diagnostics_issue('docx.revisions.structural', 'Structural revisions', 'Moved content plus unsupported character formatting, paragraph formatting, numbering, section, row, cell, and table-property revisions may be normalized; Work currently reviews body-text insertions/deletions and bounded text moves, character-, paragraph-, and ordered-list-numbering subsets.'));
2244
2255
  if (captionDiagnostics.hasUnsupportedFields) issues.push(work_office_diagnostics_issue('docx.fields', 'Fields', 'Fields beyond supported body fields, citations, bibliographies, caption SEQ fields, and bookmark or caption REF fields are converted to their current displayed value.'));
2245
2256
  const sectionProperties = descendants(document, 'sectPr');
2246
2257
  const columnProperties = descendants(document, 'cols');
package/dist/4174.js CHANGED
@@ -10096,12 +10096,23 @@ const DocumentChange = Mark.create({
10096
10096
  parseHTML: (element)=>{
10097
10097
  const declared = element.getAttribute('data-change-kind');
10098
10098
  if ('formatting' === declared) return 'formatting';
10099
+ if ('move' === declared) return 'move';
10099
10100
  return 'del' === element.tagName.toLowerCase() ? 'deletion' : 'insertion';
10100
10101
  },
10101
10102
  renderHTML: (attributes)=>({
10102
10103
  'data-change-kind': attributes.kind
10103
10104
  })
10104
10105
  },
10106
+ moveRole: {
10107
+ default: '',
10108
+ parseHTML: (element)=>{
10109
+ const role = element.getAttribute('data-change-move-role');
10110
+ return 'from' === role || 'to' === role ? role : '';
10111
+ },
10112
+ renderHTML: (attributes)=>'from' === attributes.moveRole || 'to' === attributes.moveRole ? {
10113
+ 'data-change-move-role': attributes.moveRole
10114
+ } : {}
10115
+ },
10105
10116
  id: {
10106
10117
  default: '',
10107
10118
  parseHTML: (element)=>element.getAttribute('data-change-id') ?? '',
@@ -10189,7 +10200,7 @@ const DocumentChange = Mark.create({
10189
10200
  ];
10190
10201
  },
10191
10202
  renderHTML ({ mark, HTMLAttributes }) {
10192
- const tag = 'deletion' === mark.attrs.kind ? 'del' : 'formatting' === mark.attrs.kind ? 'span' : 'ins';
10203
+ const tag = 'deletion' === mark.attrs.kind ? 'del' : 'move' === mark.attrs.kind && 'from' === mark.attrs.moveRole ? 'del' : 'formatting' === mark.attrs.kind ? 'span' : 'ins';
10193
10204
  return [
10194
10205
  tag,
10195
10206
  mergeAttributes(HTMLAttributes, {
@@ -10289,6 +10300,7 @@ const DocumentChange = Mark.create({
10289
10300
  function collectDocumentChanges(document1) {
10290
10301
  const changes = new Map();
10291
10302
  const blockChangeRanges = new Map();
10303
+ const moveTextParts = new Map();
10292
10304
  document1.descendants((node, position)=>{
10293
10305
  if (('paragraph' === node.type.name || 'heading' === node.type.name) && ('insertion' === node.attrs.blockChangeKind || 'deletion' === node.attrs.blockChangeKind)) {
10294
10306
  const kind = node.attrs.blockChangeKind;
@@ -10365,6 +10377,7 @@ function collectDocumentChanges(document1) {
10365
10377
  const mark = work_document_changes_documentChangeMark(node.marks);
10366
10378
  if (!mark) return;
10367
10379
  const kind = changeKind(mark.attrs.kind);
10380
+ const moveRole = documentMoveRole(mark.attrs.moveRole);
10368
10381
  const id = work_document_changes_stringAttribute(mark.attrs.id) || `change-at-${position}`;
10369
10382
  const key = `${kind}:${id}`;
10370
10383
  if (blockChangeRanges.get(key)?.some((range)=>position >= range.from && position + node.nodeSize <= range.to)) return;
@@ -10372,12 +10385,38 @@ function collectDocumentChanges(document1) {
10372
10385
  if (current) {
10373
10386
  current.from = Math.min(current.from, position);
10374
10387
  current.to = Math.max(current.to, position + node.nodeSize);
10375
- current.text += node.text;
10388
+ if ('move' === kind && moveRole) {
10389
+ const parts = moveTextParts.get(key) ?? {};
10390
+ const previous = parts[moveRole];
10391
+ parts[moveRole] = previous ? {
10392
+ text: previous.text + node.text,
10393
+ from: Math.min(previous.from, position),
10394
+ to: Math.max(previous.to, position + node.nodeSize)
10395
+ } : {
10396
+ text: node.text,
10397
+ from: position,
10398
+ to: position + node.nodeSize
10399
+ };
10400
+ moveTextParts.set(key, parts);
10401
+ if (current.moveRole !== moveRole) delete current.moveRole;
10402
+ } else current.text += node.text;
10376
10403
  return;
10377
10404
  }
10405
+ if ('move' === kind && moveRole) {
10406
+ const parts = moveTextParts.get(key) ?? {};
10407
+ parts[moveRole] = {
10408
+ text: node.text,
10409
+ from: position,
10410
+ to: position + node.nodeSize
10411
+ };
10412
+ moveTextParts.set(key, parts);
10413
+ }
10378
10414
  changes.set(key, {
10379
10415
  id,
10380
10416
  kind,
10417
+ ...moveRole ? {
10418
+ moveRole
10419
+ } : {},
10381
10420
  ...work_document_changes_stringAttribute(mark.attrs.actorId) ? {
10382
10421
  actorId: work_document_changes_stringAttribute(mark.attrs.actorId)
10383
10422
  } : {},
@@ -10385,9 +10424,27 @@ function collectDocumentChanges(document1) {
10385
10424
  date: work_document_changes_stringAttribute(mark.attrs.date),
10386
10425
  from: position,
10387
10426
  to: position + node.nodeSize,
10388
- text: node.text
10427
+ text: node.text ?? ''
10389
10428
  });
10390
10429
  });
10430
+ for (const [key, parts] of moveTextParts){
10431
+ const change = changes.get(key);
10432
+ if (!change) continue;
10433
+ const source = parts.from;
10434
+ const destination = parts.to;
10435
+ if (source && destination) {
10436
+ change.text = source.text === destination.text ? source.text : `${source.text} → ${destination.text}`;
10437
+ change.from = destination.from;
10438
+ change.to = destination.to;
10439
+ } else {
10440
+ const only = source ?? destination;
10441
+ if (only) {
10442
+ change.text = only.text;
10443
+ change.from = only.from;
10444
+ change.to = only.to;
10445
+ }
10446
+ }
10447
+ }
10391
10448
  return Array.from(changes.values()).sort((left, right)=>left.from - right.from);
10392
10449
  }
10393
10450
  function resolveAllDocumentChanges(document1, decision) {
@@ -10409,7 +10466,7 @@ function resolveDocumentChangesTransaction(state, tr, type, decision, ids) {
10409
10466
  const numberingSegments = numberingChangeSegments(state.doc).filter((segment)=>!ids || ids.has(segment.id));
10410
10467
  const blockSegments = blockChangeSegments(state.doc).filter((segment)=>!ids || ids.has(segment.id));
10411
10468
  if (!segments.length && !paragraphSegments.length && !numberingSegments.length && !blockSegments.length) return 0;
10412
- if (paragraphSegments.some((segment)=>!parseDocumentParagraphFormatting(segment.before)) || numberingSegments.some((segment)=>!parseDocumentNumberingChange(segment.before)) || 'reject' === decision && segments.some((segment)=>'formatting' === segment.kind && !parseDocumentCharacterFormatting(segment.before))) return 0;
10469
+ if (paragraphSegments.some((segment)=>!parseDocumentParagraphFormatting(segment.before)) || numberingSegments.some((segment)=>!parseDocumentNumberingChange(segment.before)) || 'reject' === decision && segments.some((segment)=>'formatting' === segment.kind && !parseDocumentCharacterFormatting(segment.before)) || segments.some((segment)=>'move' === segment.kind && 'from' !== segment.moveRole && 'to' !== segment.moveRole) || !validMoveSegments(segments)) return 0;
10413
10470
  closeHistory(tr);
10414
10471
  tr.setMeta(documentChangePluginKey, {
10415
10472
  decision
@@ -10425,6 +10482,12 @@ function resolveDocumentChangesTransaction(state, tr, type, decision, ids) {
10425
10482
  if ('reject' === decision) formattingRejections.push(segment);
10426
10483
  continue;
10427
10484
  }
10485
+ if ('move' === segment.kind) {
10486
+ const source = 'from' === segment.moveRole;
10487
+ const removeContent = 'accept' === decision ? source : !source;
10488
+ (removeContent ? contentDeletions : markRemovals).push(segment);
10489
+ continue;
10490
+ }
10428
10491
  const removeMark = 'accept' === decision && 'insertion' === segment.kind || 'reject' === decision && 'deletion' === segment.kind;
10429
10492
  (removeMark ? markRemovals : contentDeletions).push(segment);
10430
10493
  }
@@ -10507,16 +10570,35 @@ function documentChangeSegments(document1, type) {
10507
10570
  if (!node.isText) return;
10508
10571
  const mark = node.marks.find((candidate)=>candidate.type === type);
10509
10572
  if (!mark) return;
10573
+ const moveRole = documentMoveRole(mark.attrs.moveRole);
10510
10574
  segments.push({
10511
10575
  id: work_document_changes_stringAttribute(mark.attrs.id) || `change-at-${position}`,
10512
10576
  kind: changeKind(mark.attrs.kind),
10577
+ ...moveRole ? {
10578
+ moveRole
10579
+ } : {},
10513
10580
  from: position,
10514
10581
  to: position + node.nodeSize,
10515
- before: work_document_changes_stringAttribute(mark.attrs.before)
10582
+ before: work_document_changes_stringAttribute(mark.attrs.before),
10583
+ text: node.text ?? ''
10516
10584
  });
10517
10585
  });
10518
10586
  return segments;
10519
10587
  }
10588
+ function validMoveSegments(segments) {
10589
+ const moves = new Map();
10590
+ for (const segment of segments){
10591
+ if ('move' !== segment.kind) continue;
10592
+ if ('from' !== segment.moveRole && 'to' !== segment.moveRole) return false;
10593
+ const current = moves.get(segment.id) ?? {
10594
+ from: '',
10595
+ to: ''
10596
+ };
10597
+ current[segment.moveRole] += segment.text;
10598
+ moves.set(segment.id, current);
10599
+ }
10600
+ return Array.from(moves.values()).every(({ from, to })=>Boolean(from && to && from === to));
10601
+ }
10520
10602
  function paragraphChangeSegments(document1) {
10521
10603
  const segments = [];
10522
10604
  document1.descendants((node, position)=>{
@@ -10613,9 +10695,12 @@ function work_document_changes_documentChangeMark(marks) {
10613
10695
  return marks.find((mark)=>'documentChange' === mark.type.name);
10614
10696
  }
10615
10697
  function changeKind(value) {
10616
- if ('deletion' === value || 'formatting' === value) return value;
10698
+ if ('deletion' === value || 'formatting' === value || 'move' === value) return value;
10617
10699
  return 'insertion';
10618
10700
  }
10701
+ function documentMoveRole(value) {
10702
+ return 'from' === value || 'to' === value ? value : null;
10703
+ }
10619
10704
  function paragraphChangeAttribute(field, htmlName) {
10620
10705
  const modelName = `paragraphChange${field[0]?.toUpperCase() ?? ''}${field.slice(1)}`;
10621
10706
  return {
@@ -21439,9 +21524,9 @@ function changeIdentity(decision) {
21439
21524
  return `${decision.changeKind}:${decision.changeId}`;
21440
21525
  }
21441
21526
  function office_document_collaboration_change_decisions_changeKind(value, shared) {
21442
- if ('insertion' === value || 'deletion' === value || 'formatting' === value || 'paragraph-formatting' === value || 'numbering' === value) return value;
21527
+ if ('insertion' === value || 'deletion' === value || 'formatting' === value || 'paragraph-formatting' === value || 'numbering' === value || 'move' === value) return value;
21443
21528
  if (shared) invalidSharedSidecars('tracked-change decision kind');
21444
- invalidInputSidecars('an insertion, deletion, formatting, paragraph-formatting, or numbering tracked-change kind');
21529
+ invalidInputSidecars('an insertion, deletion, formatting, paragraph-formatting, numbering, or move tracked-change kind');
21445
21530
  }
21446
21531
  function decisionAction(value, shared) {
21447
21532
  if ('accept' === value || 'reject' === value) return value;
@@ -1,20 +1,66 @@
1
1
  import type { Editor } from '@tiptap/core';
2
+ import type { Transaction } from '@tiptap/pm/state';
3
+ export interface ControlledCompositionRange {
4
+ from: number;
5
+ to: number;
6
+ }
7
+ export interface ControlledCompositionSnapshot {
8
+ id: number;
9
+ range: ControlledCompositionRange | null;
10
+ text: string | null;
11
+ }
12
+ type CompositionSettledHandler = (editor: Editor, snapshot: ControlledCompositionSnapshot) => void;
2
13
  /**
3
- * Keeps controlled TipTap editors from publishing or reconciling content while
4
- * the browser and ProseMirror still own an IME composition. The zero-delay
5
- * settlement lets ProseMirror flush WebKit's pending DOM records first; the
6
- * bounded retries cover runtimes that keep `view.composing` true briefly after
7
- * `compositionend` without adding work to ordinary keyboard input.
14
+ * Tracks the browser-owned part of a controlled TipTap composition.
15
+ *
16
+ * WebKit can expose the provisional Latin spelling to ProseMirror, dispatch
17
+ * `compositionend`, and only then deliver the committed CJK text. Merely
18
+ * delaying `onChange` leaves both strings in the document. This controller
19
+ * keeps the original composition range mapped through every transaction and
20
+ * gives the owning editor one bounded opportunity to replace that range with
21
+ * the committed text after the browser has finished its DOM flush.
8
22
  */
9
23
  export declare class ControlledEditorComposition {
10
24
  private active;
25
+ private committedInputSeen;
26
+ private committedText;
27
+ private compositionData;
28
+ private deadline;
11
29
  private generation;
30
+ private awaitingCommittedTransaction;
31
+ private range;
32
+ private rangeFrozen;
12
33
  private settling;
34
+ private settleAt;
35
+ private settleHandler;
36
+ private settleEditor;
37
+ private sessionId;
13
38
  private timer;
14
- start(): void;
15
- end(editor: Editor, onSettled: (editor: Editor) => void): void;
39
+ /** Start tracking the selection that the browser is about to compose over. */
40
+ start(editor?: Editor | null): void;
41
+ /**
42
+ * Keep the composition range aligned with ProseMirror's current document.
43
+ * This includes the transaction generated when a browser first exposes the
44
+ * provisional pinyin and the transaction generated by a late committed
45
+ * input event.
46
+ */
47
+ recordTransaction(transaction: Transaction): void;
48
+ /** Observe beforeinput/input without taking ownership of the browser event. */
49
+ noteInput(event: Event): void;
50
+ /**
51
+ * End a composition. The two-argument form is retained for the Markdown and
52
+ * presentation editors; document editing passes compositionend.data as the
53
+ * middle argument so late WebKit input can be distinguished from typing.
54
+ */
55
+ end(editor: Editor, dataOrHandler: string | null | undefined | CompositionSettledHandler, maybeHandler?: CompositionSettledHandler): void;
16
56
  isBlocking(editor?: Editor | null): boolean;
17
57
  destroy(): void;
58
+ private selectionRange;
59
+ private currentRangeText;
60
+ private snapshot;
61
+ private scheduleQuietSettlement;
18
62
  private scheduleSettlement;
63
+ private clearSettlement;
19
64
  private cancelTimer;
20
65
  }
66
+ export {};
@@ -31,9 +31,11 @@ export interface DocumentPaginationPageDescriptorDerivation {
31
31
  derivedPageCount: number;
32
32
  }
33
33
  export interface UseDocumentPaginationOptions {
34
+ compositionRevision?: number;
34
35
  editor: Editor | null;
35
36
  documentRevision: number;
36
37
  enabled: boolean;
38
+ isComposing?: () => boolean;
37
39
  layoutKey: string;
38
40
  page: OfficeKernelPageMetrics;
39
41
  selectionVersion: number;
@@ -49,7 +51,7 @@ export interface UseDocumentPaginationValue {
49
51
  paginating: boolean;
50
52
  resolveFieldContext: WorkDocumentFieldContextResolver | null;
51
53
  }
52
- export declare function useDocumentPagination({ editor, documentRevision, enabled, layoutKey, page, selectionVersion, wasmUrl, layoutFonts, loadedLayoutFontIds, }: UseDocumentPaginationOptions): UseDocumentPaginationValue;
54
+ export declare function useDocumentPagination({ compositionRevision, editor, documentRevision, enabled, isComposing, layoutKey, page, selectionVersion, wasmUrl, layoutFonts, loadedLayoutFontIds, }: UseDocumentPaginationOptions): UseDocumentPaginationValue;
53
55
  export declare function createDocumentFieldPaginationContextResolver(pagination: DocumentPaginationResult): WorkDocumentFieldContextResolver;
54
56
  export declare function documentResizeObservationPositions(blocks: readonly MeasuredDocumentLayoutBlock[]): ReadonlyMap<HTMLElement, number>;
55
57
  export declare function documentResizeObservationTargets(editorDom: HTMLElement, blocks: readonly MeasuredDocumentLayoutBlock[]): {
@@ -1,7 +1,7 @@
1
1
  import { Mark } from '@tiptap/core';
2
2
  import { type Node as ProseMirrorNode } from '@tiptap/pm/model';
3
- import type { WorkDocumentChangeKind } from './work-types';
4
- export type { WorkDocumentChangeKind } from './work-types';
3
+ import type { WorkDocumentChangeKind, WorkDocumentMoveRole } from './work-types';
4
+ export type { WorkDocumentChangeKind, WorkDocumentMoveRole, } from './work-types';
5
5
  export interface WorkDocumentChangeIdentity {
6
6
  id: string;
7
7
  actorId?: string;
@@ -10,6 +10,8 @@ export interface WorkDocumentChangeIdentity {
10
10
  }
11
11
  export interface WorkDocumentChange extends WorkDocumentChangeIdentity {
12
12
  kind: WorkDocumentChangeKind;
13
+ /** Present when a change is one side of a native Word move revision. */
14
+ moveRole?: WorkDocumentMoveRole;
13
15
  from: number;
14
16
  to: number;
15
17
  text: string;
@@ -1,6 +1,7 @@
1
- import type { WorkDocumentChangeIdentity, WorkDocumentChangeKind } from './work-document-changes';
1
+ import type { WorkDocumentChangeIdentity, WorkDocumentChangeKind, WorkDocumentMoveRole } from './work-document-changes';
2
2
  export interface ImportedDocxChangeMarker extends WorkDocumentChangeIdentity {
3
3
  kind: WorkDocumentChangeKind;
4
+ moveRole?: WorkDocumentMoveRole;
4
5
  start: string;
5
6
  end: string;
6
7
  }
@@ -10,3 +11,11 @@ export interface ImportedDocxChangeMarkers {
10
11
  export declare function markDocxTextChanges(document: Document): ImportedDocxChangeMarkers;
11
12
  export declare function applyImportedDocxChangeMarkers(document: Document, markers: ImportedDocxChangeMarkers): void;
12
13
  export declare function hasImportedDocxChangeMarkers(markers: ImportedDocxChangeMarkers): boolean;
14
+ /**
15
+ * Returns whether one move wrapper uses the bounded, text-only Word shape.
16
+ * Range-marker moves and relationship-bearing children intentionally remain on
17
+ * the compatibility path until their structural semantics are modelled.
18
+ */
19
+ export declare function isSupportedDocxMoveChange(element: Element): boolean;
20
+ /** Counts complete, paired move revisions in a WordprocessingML story. */
21
+ export declare function supportedDocxMovePairCount(document: Document): number;
@@ -0,0 +1,35 @@
1
+ export type DocxMoveRevisionRole = 'from' | 'to';
2
+ export interface DocxMoveRevisionRegistration {
3
+ kind: 'move-from' | 'move-to';
4
+ wireId: number;
5
+ id: number;
6
+ author: string;
7
+ date: string;
8
+ }
9
+ export interface DocxMoveRevisionPatch {
10
+ wireId: number;
11
+ id: number;
12
+ role: DocxMoveRevisionRole;
13
+ author: string;
14
+ date: string;
15
+ }
16
+ /**
17
+ * Allocates transient revision IDs while the `docx` package creates its
18
+ * ordinary `w:ins`/`w:del` wrappers. The post-pack patcher changes those
19
+ * wrappers to native `w:moveFrom`/`w:moveTo` elements and restores the shared
20
+ * logical revision ID. Negative wire IDs cannot collide with the positive
21
+ * IDs emitted for ordinary tracked changes.
22
+ */
23
+ export declare class DocxMoveRevisionPatchCollector {
24
+ readonly patches: DocxMoveRevisionPatch[];
25
+ private readonly registrations;
26
+ private nextWireId;
27
+ register(element: HTMLElement, id: number, revisionDate?: string): DocxMoveRevisionRegistration | null;
28
+ }
29
+ /**
30
+ * Converts the transient wrappers emitted by `docx` into native Word move
31
+ * revisions. Every registered side must be emitted at least once; a missing
32
+ * side is treated as a programming or package-generation error rather than
33
+ * silently producing a half-move that Word would interpret differently.
34
+ */
35
+ export declare function patchDocxMoveRevisions(buffer: ArrayBuffer, patches: readonly DocxMoveRevisionPatch[]): Promise<ArrayBuffer>;
@@ -106,7 +106,9 @@ export interface WorkDocumentContent {
106
106
  comments?: WorkDocumentComment[];
107
107
  bibliography?: WorkDocumentBibliography;
108
108
  }
109
- export type WorkDocumentChangeKind = 'insertion' | 'deletion' | 'formatting' | 'paragraph-formatting' | 'numbering';
109
+ export type WorkDocumentChangeKind = 'insertion' | 'deletion' | 'formatting' | 'paragraph-formatting' | 'numbering' | 'move';
110
+ /** The side of a native Word move revision represented by a text mark. */
111
+ export type WorkDocumentMoveRole = 'from' | 'to';
110
112
  export type WorkDocumentChangeDecisionAction = 'accept' | 'reject';
111
113
  /**
112
114
  * Immutable audit record created when an editor accepts or rejects one
Binary file