@extend-ai/react-docx 0.6.5 → 0.6.7

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/dist/index.js CHANGED
@@ -12187,7 +12187,15 @@ var defaultStarterModel = {
12187
12187
  headerSections: [],
12188
12188
  footerSections: [],
12189
12189
  paragraphStyles: [
12190
- { id: "Normal", name: "Body", isDefault: true },
12190
+ {
12191
+ id: "Normal",
12192
+ name: "Body",
12193
+ isDefault: true,
12194
+ // Word's blank-document default body typeface. Without this the body
12195
+ // default fell through to a heading font / Times New Roman and did not
12196
+ // match the "Calibri" shown in the toolbar.
12197
+ runStyle: { fontFamily: "Calibri", fontSizePt: 11 }
12198
+ },
12191
12199
  {
12192
12200
  id: "Heading1",
12193
12201
  name: "Heading 1",
@@ -12330,7 +12338,7 @@ function resolveDocumentInheritedFontFamily(model) {
12330
12338
  return cssFontFamily(defaultStyleFontFamily);
12331
12339
  }
12332
12340
  const paragraphStyleFontFamily = paragraphStyles.find(
12333
- (style) => Boolean(style.runStyle?.fontFamily?.trim())
12341
+ (style) => style.headingLevel === void 0 && Boolean(style.runStyle?.fontFamily?.trim())
12334
12342
  )?.runStyle?.fontFamily;
12335
12343
  if (paragraphStyleFontFamily) {
12336
12344
  return cssFontFamily(paragraphStyleFontFamily);
@@ -14565,7 +14573,17 @@ function nodeAlreadyEndsAtExplicitPageBoundary(node) {
14565
14573
  }
14566
14574
  return paragraphHasExplicitPageBreak2(node) || paragraphHasPageBreakBefore2(node) || sectionBreakAfterParagraphStartsNewPage(node);
14567
14575
  }
14576
+ var docxHardPageBreakStartNodeIndexesByModel = /* @__PURE__ */ new WeakMap();
14568
14577
  function collectDocxHardPageBreakStartNodeIndexes(model) {
14578
+ const cached = docxHardPageBreakStartNodeIndexesByModel.get(model);
14579
+ if (cached) {
14580
+ return cached;
14581
+ }
14582
+ const result = computeDocxHardPageBreakStartNodeIndexes(model);
14583
+ docxHardPageBreakStartNodeIndexesByModel.set(model, result);
14584
+ return result;
14585
+ }
14586
+ function computeDocxHardPageBreakStartNodeIndexes(model) {
14569
14587
  const breaks = collectTopLevelExplicitPageBreakStartNodeIndexes(model.nodes);
14570
14588
  const sections = resolveDocumentSectionsFromMetadata(model.metadata);
14571
14589
  for (let sectionIndex = 1; sectionIndex < sections.length; sectionIndex += 1) {
@@ -14589,7 +14607,17 @@ function collectDocxHardPageBreakStartNodeIndexes(model) {
14589
14607
  }
14590
14608
  return breaks;
14591
14609
  }
14610
+ var docxSectionStartPageBreakNodeIndexesByModel = /* @__PURE__ */ new WeakMap();
14592
14611
  function collectDocxSectionStartPageBreakNodeIndexes(model) {
14612
+ const cached = docxSectionStartPageBreakNodeIndexesByModel.get(model);
14613
+ if (cached) {
14614
+ return cached;
14615
+ }
14616
+ const result = computeDocxSectionStartPageBreakNodeIndexes(model);
14617
+ docxSectionStartPageBreakNodeIndexesByModel.set(model, result);
14618
+ return result;
14619
+ }
14620
+ function computeDocxSectionStartPageBreakNodeIndexes(model) {
14593
14621
  const breaks = /* @__PURE__ */ new Set();
14594
14622
  const sections = resolveDocumentSectionsFromMetadata(model.metadata);
14595
14623
  for (let sectionIndex = 1; sectionIndex < sections.length; sectionIndex += 1) {
@@ -29986,6 +30014,8 @@ function DocxEditorViewer({
29986
30014
  const fileDragDepthRef = React.useRef(0);
29987
30015
  const tableDraftLayoutRefreshRafRef = React.useRef(null);
29988
30016
  const activeRangeFlushFrameRef = React.useRef(null);
30017
+ const pendingEditableCaretRef = React.useRef(null);
30018
+ const pointerSelectionDragRef = React.useRef({ down: false, moved: false });
29989
30019
  const deferredCollapsedSelectionSyncTimeoutRef = React.useRef(
29990
30020
  null
29991
30021
  );
@@ -34034,14 +34064,62 @@ function DocxEditorViewer({
34034
34064
  });
34035
34065
  }, [resolveParagraphBoundaryFromSelectionPoint]);
34036
34066
  const setActiveRangeFromSelection = React.useCallback(() => {
34067
+ const session = editor.selectionSessionKind;
34068
+ const rootElement = viewerRootRef.current;
34069
+ const activeElement = document.activeElement;
34070
+ const editorHasFocus = Boolean(
34071
+ activeElement instanceof HTMLElement && activeElement.isContentEditable && rootElement && rootElement.contains(activeElement)
34072
+ );
34073
+ const liveSelection = window.getSelection();
34074
+ const anchorNode = liveSelection?.anchorNode ?? null;
34075
+ const collapsedElementAnchor = Boolean(
34076
+ liveSelection && liveSelection.isCollapsed && liveSelection.rangeCount > 0 && anchorNode && anchorNode.nodeType !== Node.TEXT_NODE
34077
+ );
34078
+ const selectionDropped = Boolean(
34079
+ editorHasFocus && (!liveSelection || liveSelection.rangeCount === 0)
34080
+ );
34081
+ if (collapsedElementAnchor || selectionDropped) {
34082
+ if (session === "keyboard" || session === "composition") {
34083
+ return;
34084
+ }
34085
+ const anchorElement = anchorNode instanceof Element ? anchorNode : anchorNode?.parentElement ?? null;
34086
+ const destroyedHost = anchorElement?.closest(
34087
+ "[data-docx-paragraph-host='true']"
34088
+ ) ?? (editorHasFocus ? activeElement.closest(
34089
+ "[data-docx-paragraph-host='true']"
34090
+ ) : null);
34091
+ const lastGoodRange = cloneTextRange(editor.activeTextRange);
34092
+ if (lastGoodRange && destroyedHost) {
34093
+ const normalizedLastGood = normalizeTextRange(lastGoodRange);
34094
+ const lastGoodHost = resolveParagraphHostElement(
34095
+ normalizedLastGood.start.location
34096
+ );
34097
+ if (lastGoodHost === destroyedHost) {
34098
+ setSelectionFromDocxBoundaries(
34099
+ normalizedLastGood.start,
34100
+ normalizedLastGood.end
34101
+ );
34102
+ }
34103
+ }
34104
+ return;
34105
+ }
34037
34106
  const range = resolveActiveRangeFromDomSelection();
34038
34107
  if (!range) {
34108
+ if (editorHasFocus && editor.activeTextRange) {
34109
+ return;
34110
+ }
34039
34111
  editor.setActiveTextRange(void 0);
34040
34112
  return;
34041
34113
  }
34042
34114
  clearTableCellSelection();
34043
34115
  editor.setActiveTextRange(range);
34044
- }, [clearTableCellSelection, editor, resolveActiveRangeFromDomSelection]);
34116
+ }, [
34117
+ clearTableCellSelection,
34118
+ editor,
34119
+ resolveActiveRangeFromDomSelection,
34120
+ resolveParagraphHostElement,
34121
+ setSelectionFromDocxBoundaries
34122
+ ]);
34045
34123
  const flushActiveRangeFromSelection = React.useCallback(() => {
34046
34124
  if (deferredCollapsedSelectionSyncTimeoutRef.current !== null) {
34047
34125
  window.clearTimeout(deferredCollapsedSelectionSyncTimeoutRef.current);
@@ -34055,6 +34133,73 @@ function DocxEditorViewer({
34055
34133
  setActiveRangeFromSelection();
34056
34134
  });
34057
34135
  }, [setActiveRangeFromSelection]);
34136
+ React.useLayoutEffect(() => {
34137
+ if (isReadOnly) {
34138
+ return;
34139
+ }
34140
+ const session = editor.selectionSessionKind;
34141
+ if (pointerSelectionDragRef.current.moved || session === "composition") {
34142
+ return;
34143
+ }
34144
+ const rootElement = viewerRootRef.current;
34145
+ if (!rootElement) {
34146
+ return;
34147
+ }
34148
+ const activeElement = document.activeElement;
34149
+ if (!(activeElement instanceof HTMLElement) || !activeElement.isContentEditable || !rootElement.contains(activeElement)) {
34150
+ return;
34151
+ }
34152
+ const focusedHost = activeElement.closest(
34153
+ "[data-docx-paragraph-host='true']"
34154
+ );
34155
+ if (!focusedHost) {
34156
+ return;
34157
+ }
34158
+ const selection = window.getSelection();
34159
+ if (!selection) {
34160
+ return;
34161
+ }
34162
+ const liveRange = selection.rangeCount > 0 ? selection.getRangeAt(0) : void 0;
34163
+ const anchorNode = selection.anchorNode;
34164
+ const selectionInsideHost = Boolean(
34165
+ liveRange && focusedHost.contains(liveRange.startContainer) && focusedHost.contains(liveRange.endContainer)
34166
+ );
34167
+ const anchorIsElement = Boolean(
34168
+ anchorNode && anchorNode.nodeType !== Node.TEXT_NODE
34169
+ );
34170
+ const selectionWasDestroyed = !liveRange || !selectionInsideHost || selection.isCollapsed && anchorIsElement;
34171
+ if (!selectionWasDestroyed) {
34172
+ return;
34173
+ }
34174
+ const focusedNodeIndexAttr = focusedHost.getAttribute(
34175
+ "data-docx-paragraph-node-index"
34176
+ );
34177
+ const focusedNodeIndex = focusedNodeIndexAttr != null ? Number.parseInt(focusedNodeIndexAttr, 10) : Number.NaN;
34178
+ const pendingCaret = pendingEditableCaretRef.current;
34179
+ if (pendingCaret && Number.isFinite(focusedNodeIndex) && pendingCaret.nodeIndex === focusedNodeIndex) {
34180
+ const textLength = editableTextFromElement(focusedHost).length;
34181
+ const safeStart = Math.max(0, Math.min(pendingCaret.start, textLength));
34182
+ const safeEnd = Math.max(
34183
+ safeStart,
34184
+ Math.min(pendingCaret.end, textLength)
34185
+ );
34186
+ setSelectionWithinElementByTextOffsets(focusedHost, safeStart, safeEnd);
34187
+ return;
34188
+ }
34189
+ if (session === "keyboard") {
34190
+ return;
34191
+ }
34192
+ const range = editor.activeTextRange;
34193
+ if (!range) {
34194
+ return;
34195
+ }
34196
+ const normalized = normalizeTextRange(range);
34197
+ const targetHost = resolveParagraphHostElement(normalized.start.location);
34198
+ if (!targetHost || targetHost !== focusedHost) {
34199
+ return;
34200
+ }
34201
+ setSelectionFromDocxBoundaries(normalized.start, normalized.end);
34202
+ });
34058
34203
  const selectionIsExpandedWithinElement = React.useCallback(
34059
34204
  (element) => {
34060
34205
  const selection = window.getSelection();
@@ -34508,6 +34653,28 @@ function DocxEditorViewer({
34508
34653
  const collapsedCaretInsideEditableHost = Boolean(
34509
34654
  selectionRange.collapsed && activeElement instanceof HTMLElement && activeElement.isContentEditable && rootElement.contains(activeElement) && activeElement.contains(selectionRange.startContainer)
34510
34655
  );
34656
+ const selectionWithinSingleEditableHost = Boolean(
34657
+ activeElement instanceof HTMLElement && activeElement.isContentEditable && rootElement.contains(activeElement) && activeElement.contains(selectionRange.startContainer) && activeElement.contains(selectionRange.endContainer) && selectionRange.startContainer.nodeType === Node.TEXT_NODE && selectionRange.endContainer.nodeType === Node.TEXT_NODE
34658
+ );
34659
+ if (selectionWithinSingleEditableHost && activeElement instanceof HTMLElement) {
34660
+ const host = activeElement.closest(
34661
+ "[data-docx-paragraph-host='true']"
34662
+ );
34663
+ const nodeIndexAttr = host?.getAttribute(
34664
+ "data-docx-paragraph-node-index"
34665
+ );
34666
+ const offsets = host ? selectionOffsetsWithinElement(host) : void 0;
34667
+ if (host && nodeIndexAttr != null && offsets) {
34668
+ const nodeIndex = Number.parseInt(nodeIndexAttr, 10);
34669
+ if (Number.isFinite(nodeIndex)) {
34670
+ pendingEditableCaretRef.current = {
34671
+ nodeIndex,
34672
+ start: offsets.start,
34673
+ end: offsets.end
34674
+ };
34675
+ }
34676
+ }
34677
+ }
34511
34678
  if (collapsedCaretInsideEditableHost) {
34512
34679
  scheduleDeferredCollapsedSelectionSync();
34513
34680
  return;
@@ -34519,6 +34686,27 @@ function DocxEditorViewer({
34519
34686
  document.removeEventListener("selectionchange", handleSelectionChange);
34520
34687
  };
34521
34688
  }, [flushActiveRangeFromSelection, scheduleDeferredCollapsedSelectionSync]);
34689
+ React.useEffect(() => {
34690
+ const onPointerDown = () => {
34691
+ pointerSelectionDragRef.current = { down: true, moved: false };
34692
+ };
34693
+ const onPointerMove = (event) => {
34694
+ if (pointerSelectionDragRef.current.down && event.buttons !== 0) {
34695
+ pointerSelectionDragRef.current.moved = true;
34696
+ }
34697
+ };
34698
+ const onPointerUp = () => {
34699
+ pointerSelectionDragRef.current = { down: false, moved: false };
34700
+ };
34701
+ window.addEventListener("pointerdown", onPointerDown, true);
34702
+ window.addEventListener("pointermove", onPointerMove, true);
34703
+ window.addEventListener("pointerup", onPointerUp, true);
34704
+ return () => {
34705
+ window.removeEventListener("pointerdown", onPointerDown, true);
34706
+ window.removeEventListener("pointermove", onPointerMove, true);
34707
+ window.removeEventListener("pointerup", onPointerUp, true);
34708
+ };
34709
+ }, []);
34522
34710
  const beginCrossNodeSelectionDrag = React.useCallback(
34523
34711
  (startBoundary, pointerId, startX, startY) => {
34524
34712
  tableSelectionDragRef.current = {
@@ -36754,6 +36942,8 @@ function DocxEditorViewer({
36754
36942
  if (Date.now() < paginationMeasurementSuspendUntilRef.current) {
36755
36943
  return;
36756
36944
  }
36945
+ const rootElement = viewerRootRef.current;
36946
+ const zoomScale = rootElement ? resolveEffectiveZoomScale(rootElement) : virtualizerMeasurementScale;
36757
36947
  const nextMeasuredHeights = {};
36758
36948
  editor.model.nodes.forEach((node, nodeIndex) => {
36759
36949
  if (node.type !== "table") {
@@ -36769,7 +36959,8 @@ function DocxEditorViewer({
36769
36959
  if (normalizedRowIndex >= node.rows.length || !visibleRowIndexes.has(normalizedRowIndex)) {
36770
36960
  return;
36771
36961
  }
36772
- const normalizedHeight = normalizeMeasuredTableRowHeightPx(rowHeightPx);
36962
+ const documentRowHeightPx = Number.isFinite(zoomScale) && zoomScale > 0 ? rowHeightPx / zoomScale : rowHeightPx;
36963
+ const normalizedHeight = normalizeMeasuredTableRowHeightPx(documentRowHeightPx);
36773
36964
  const previousHeight = measuredByRowIndex.get(normalizedRowIndex);
36774
36965
  measuredByRowIndex.set(
36775
36966
  normalizedRowIndex,
@@ -36898,6 +37089,7 @@ function DocxEditorViewer({
36898
37089
  tableColumnWidths,
36899
37090
  tableRowHeights,
36900
37091
  tableDraftLayoutEpoch,
37092
+ virtualizerMeasurementScale,
36901
37093
  visibleTableRowIndexesByNodeIndex
36902
37094
  ]);
36903
37095
  const normalizeResizableHeights = React.useCallback(
@@ -41946,6 +42138,18 @@ function DocxEditorViewer({
41946
42138
  ...leadingCoverLayoutSpacer ? {
41947
42139
  minHeight: `${estimateParagraphLineHeightPx(node, nodeDocGridLinePitchPx) + EMPTY_PARAGRAPH_EXTRA_HEIGHT_PX + LEADING_COVER_SPACER_EXTRA_HEIGHT_PX}px`
41948
42140
  } : void 0,
42141
+ // Carry the paragraph's run font on the editable host so text typed into
42142
+ // it (which is a bare, not-yet-committed text node, not a styled run
42143
+ // span) renders in the same font the committed run will use — i.e. the
42144
+ // font shown in the toolbar — instead of inheriting a generic default.
42145
+ // Rendered runs set their own font on their span, so this only affects
42146
+ // freshly typed/empty content. When the paragraph has no explicit run
42147
+ // font we leave it unset so the document default still applies.
42148
+ ...(() => {
42149
+ const runStyle = firstRunStyle(node);
42150
+ const hostFontFamily = cssFontFamily(runStyle?.fontFamily);
42151
+ return hostFontFamily ? { fontFamily: hostFontFamily } : {};
42152
+ })(),
41949
42153
  outline: "none"
41950
42154
  };
41951
42155
  let fallbackParagraphRuns;
@@ -42171,6 +42375,16 @@ function DocxEditorViewer({
42171
42375
  if (!editable) {
42172
42376
  return;
42173
42377
  }
42378
+ const mouseUpOffsets = selectionOffsetsWithinElement(
42379
+ event.currentTarget
42380
+ );
42381
+ if (mouseUpOffsets) {
42382
+ pendingEditableCaretRef.current = {
42383
+ nodeIndex,
42384
+ start: mouseUpOffsets.start,
42385
+ end: mouseUpOffsets.end
42386
+ };
42387
+ }
42174
42388
  if (selectionIsExpandedWithinElement(event.currentTarget)) {
42175
42389
  flushActiveRangeFromSelection();
42176
42390
  cancelPendingPointerSelectionReconcile();
@@ -42215,6 +42429,14 @@ function DocxEditorViewer({
42215
42429
  nodeIndex,
42216
42430
  event.currentTarget.innerHTML
42217
42431
  );
42432
+ const typedOffsets = selectionOffsetsWithinElement(
42433
+ event.currentTarget
42434
+ );
42435
+ pendingEditableCaretRef.current = typedOffsets ? {
42436
+ nodeIndex,
42437
+ start: typedOffsets.start,
42438
+ end: typedOffsets.end
42439
+ } : null;
42218
42440
  },
42219
42441
  onCompositionStart: () => {
42220
42442
  if (!editable) {