@harbour-enterprises/superdoc 1.4.0 → 1.4.1-next.1

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.
@@ -7257,8 +7257,11 @@ const validXmlAttributes$e = [
7257
7257
  createAttributeHandler("w:authorEmail", "authorEmail")
7258
7258
  ];
7259
7259
  const encode$K = (params, encodedAttrs = {}) => {
7260
- const { nodeListHandler, extraParams = {} } = params;
7260
+ const { nodeListHandler, extraParams = {}, converter } = params;
7261
7261
  const { node } = extraParams;
7262
+ if (encodedAttrs.id && converter?.trackedChangeIdMap?.has(encodedAttrs.id)) {
7263
+ encodedAttrs.id = converter.trackedChangeIdMap.get(encodedAttrs.id);
7264
+ }
7262
7265
  const subs = nodeListHandler.handler({
7263
7266
  ...params,
7264
7267
  insideTrackChange: true,
@@ -7266,6 +7269,9 @@ const encode$K = (params, encodedAttrs = {}) => {
7266
7269
  path: [...params.path || [], node]
7267
7270
  });
7268
7271
  encodedAttrs.importedAuthor = `${encodedAttrs.author} (imported)`;
7272
+ if (converter?.documentOrigin) {
7273
+ encodedAttrs.origin = converter.documentOrigin;
7274
+ }
7269
7275
  subs.forEach((subElement) => {
7270
7276
  subElement.marks = [];
7271
7277
  if (subElement?.content?.[0]) {
@@ -27325,8 +27331,11 @@ const validXmlAttributes$2 = [
27325
27331
  createAttributeHandler("w:authorEmail", "authorEmail")
27326
27332
  ];
27327
27333
  const encode$2 = (params, encodedAttrs = {}) => {
27328
- const { nodeListHandler, extraParams = {} } = params;
27334
+ const { nodeListHandler, extraParams = {}, converter } = params;
27329
27335
  const { node } = extraParams;
27336
+ if (encodedAttrs.id && converter?.trackedChangeIdMap?.has(encodedAttrs.id)) {
27337
+ encodedAttrs.id = converter.trackedChangeIdMap.get(encodedAttrs.id);
27338
+ }
27330
27339
  const subs = nodeListHandler.handler({
27331
27340
  ...params,
27332
27341
  insideTrackChange: true,
@@ -27334,6 +27343,9 @@ const encode$2 = (params, encodedAttrs = {}) => {
27334
27343
  path: [...params.path || [], node]
27335
27344
  });
27336
27345
  encodedAttrs.importedAuthor = `${encodedAttrs.author} (imported)`;
27346
+ if (converter?.documentOrigin) {
27347
+ encodedAttrs.origin = converter.documentOrigin;
27348
+ }
27337
27349
  subs.forEach((subElement) => {
27338
27350
  subElement.marks = [];
27339
27351
  if (subElement?.content?.[0]) {
@@ -28053,6 +28065,8 @@ function importCommentData({ docx, editor, converter }) {
28053
28065
  });
28054
28066
  const lastElement = parsedElements[parsedElements.length - 1];
28055
28067
  const paraId = lastElement?.attrs?.["w14:paraId"];
28068
+ const commentsExtended = docx["word/commentsExtended.xml"];
28069
+ const threadingMethod = commentsExtended ? "commentsExtended" : "range-based";
28056
28070
  return {
28057
28071
  commentId: internalId || uuid.v4(),
28058
28072
  importedId,
@@ -28067,19 +28081,31 @@ function importCommentData({ docx, editor, converter }) {
28067
28081
  trackedChangeText,
28068
28082
  trackedChangeType,
28069
28083
  trackedDeletedText,
28070
- isDone: false
28084
+ isDone: false,
28085
+ origin: converter?.documentOrigin || "word",
28086
+ threadingMethod,
28087
+ originalXmlStructure: {
28088
+ hasCommentsExtended: !!commentsExtended,
28089
+ hasCommentsExtensible: !!docx["word/commentsExtensible.xml"],
28090
+ hasCommentsIds: !!docx["word/commentsIds.xml"]
28091
+ }
28071
28092
  };
28072
28093
  });
28073
- const extendedComments = generateCommentsWithExtendedData({ docx, comments: extractedComments });
28094
+ const extendedComments = generateCommentsWithExtendedData({ docx, comments: extractedComments, converter });
28074
28095
  return extendedComments;
28075
28096
  }
28076
- const generateCommentsWithExtendedData = ({ docx, comments }) => {
28097
+ const generateCommentsWithExtendedData = ({ docx, comments, converter }) => {
28077
28098
  if (!comments?.length) return [];
28099
+ const rangeData = extractCommentRangesFromDocument(docx, converter);
28100
+ const { commentsInTrackedChanges } = rangeData;
28101
+ const trackedChangeParentMap = detectThreadingFromTrackedChanges(comments, commentsInTrackedChanges);
28078
28102
  const commentsExtended = docx["word/commentsExtended.xml"];
28079
28103
  if (!commentsExtended) {
28080
- const commentRanges = extractCommentRangesFromDocument(docx);
28081
- const commentsWithThreading = detectThreadingFromRanges(comments, commentRanges);
28082
- return commentsWithThreading.map((comment) => ({ ...comment, isDone: comment.isDone ?? false }));
28104
+ const commentsWithThreading = detectThreadingFromRanges(comments, rangeData);
28105
+ return commentsWithThreading.map((comment) => ({
28106
+ ...comment,
28107
+ isDone: comment.isDone ?? false
28108
+ }));
28083
28109
  }
28084
28110
  const { elements: initialElements = [] } = commentsExtended;
28085
28111
  if (!initialElements?.length) return comments.map((comment) => ({ ...comment, isDone: comment.isDone ?? false }));
@@ -28087,25 +28113,30 @@ const generateCommentsWithExtendedData = ({ docx, comments }) => {
28087
28113
  const commentEx = elements.filter((el) => el.name === "w15:commentEx");
28088
28114
  return comments.map((comment) => {
28089
28115
  const extendedDef = commentEx.find((ce2) => {
28090
- const isIncludedInCommentElements = comment.elements?.some(
28091
- (el) => el.attrs?.["w14:paraId"] === ce2.attributes["w15:paraId"]
28092
- );
28093
- return isIncludedInCommentElements;
28116
+ return comment.elements?.some((el) => el.attrs?.["w14:paraId"] === ce2.attributes["w15:paraId"]);
28094
28117
  });
28095
- if (!extendedDef) return { ...comment, isDone: comment.isDone ?? false };
28096
- const { isDone, paraIdParent } = getExtendedDetails(extendedDef);
28097
- let parentComment;
28098
- if (paraIdParent) {
28099
- parentComment = comments.find(
28100
- (c) => c.paraId === paraIdParent || c.elements?.some((el) => el.attrs?.["w14:paraId"] === paraIdParent)
28101
- );
28118
+ let isDone = comment.isDone ?? false;
28119
+ let parentCommentId = void 0;
28120
+ const trackedChangeParent = trackedChangeParentMap.get(comment.importedId);
28121
+ const isInsideTrackedChange = trackedChangeParent?.isTrackedChangeParent;
28122
+ if (extendedDef) {
28123
+ const details = getExtendedDetails(extendedDef);
28124
+ isDone = details.isDone ?? false;
28125
+ if (!isInsideTrackedChange && details.paraIdParent) {
28126
+ const parentComment = comments.find(
28127
+ (c) => c.paraId === details.paraIdParent || c.elements?.some((el) => el.attrs?.["w14:paraId"] === details.paraIdParent)
28128
+ );
28129
+ parentCommentId = parentComment?.commentId;
28130
+ }
28131
+ }
28132
+ if (isInsideTrackedChange) {
28133
+ parentCommentId = trackedChangeParent.trackedChangeId;
28102
28134
  }
28103
- const newComment = {
28135
+ return {
28104
28136
  ...comment,
28105
- isDone: isDone ?? false,
28106
- parentCommentId: parentComment?.commentId
28137
+ isDone,
28138
+ parentCommentId
28107
28139
  };
28108
- return newComment;
28109
28140
  });
28110
28141
  };
28111
28142
  const getExtendedDetails = (commentEx) => {
@@ -28115,34 +28146,117 @@ const getExtendedDetails = (commentEx) => {
28115
28146
  const paraIdParent = attributes["w15:paraIdParent"];
28116
28147
  return { paraId, isDone, paraIdParent };
28117
28148
  };
28118
- const extractCommentRangesFromDocument = (docx) => {
28149
+ const extractCommentRangesFromDocument = (docx, converter) => {
28119
28150
  const documentXml = docx["word/document.xml"];
28120
28151
  if (!documentXml) {
28121
- return [];
28122
- }
28123
- const pendingComments = [];
28124
- const walkElements = (elements) => {
28152
+ return { rangeEvents: [], rangePositions: /* @__PURE__ */ new Map(), commentsInTrackedChanges: /* @__PURE__ */ new Map() };
28153
+ }
28154
+ const rangeEvents = [];
28155
+ const rangePositions = /* @__PURE__ */ new Map();
28156
+ const commentsInTrackedChanges = /* @__PURE__ */ new Map();
28157
+ let positionIndex = 0;
28158
+ let lastElementWasCommentMarker = false;
28159
+ const recentlyClosedComments = /* @__PURE__ */ new Set();
28160
+ let lastTrackedChange = null;
28161
+ const walkElements = (elements, currentTrackedChangeId = null) => {
28125
28162
  if (!elements || !Array.isArray(elements)) return;
28126
28163
  elements.forEach((element) => {
28127
- if (element.name === "w:commentRangeStart") {
28164
+ const isCommentStart = element.name === "w:commentRangeStart";
28165
+ const isCommentEnd = element.name === "w:commentRangeEnd";
28166
+ const isTrackedChange = element.name === "w:ins" || element.name === "w:del";
28167
+ if (isCommentStart) {
28128
28168
  const commentId = element.attributes?.["w:id"];
28129
28169
  if (commentId !== void 0) {
28130
- pendingComments.push({
28170
+ const id = String(commentId);
28171
+ rangeEvents.push({
28131
28172
  type: "start",
28132
- commentId: String(commentId)
28173
+ commentId: id
28133
28174
  });
28175
+ if (!rangePositions.has(id)) {
28176
+ rangePositions.set(id, { startIndex: positionIndex, endIndex: -1 });
28177
+ } else {
28178
+ rangePositions.get(id).startIndex = positionIndex;
28179
+ }
28180
+ if (currentTrackedChangeId !== null) {
28181
+ commentsInTrackedChanges.set(id, currentTrackedChangeId);
28182
+ }
28134
28183
  }
28135
- } else if (element.name === "w:commentRangeEnd") {
28184
+ lastElementWasCommentMarker = true;
28185
+ recentlyClosedComments.clear();
28186
+ } else if (isCommentEnd) {
28136
28187
  const commentId = element.attributes?.["w:id"];
28137
28188
  if (commentId !== void 0) {
28138
- pendingComments.push({
28189
+ const id = String(commentId);
28190
+ rangeEvents.push({
28139
28191
  type: "end",
28140
- commentId: String(commentId)
28192
+ commentId: id
28141
28193
  });
28194
+ if (!rangePositions.has(id)) {
28195
+ rangePositions.set(id, { startIndex: -1, endIndex: positionIndex });
28196
+ } else {
28197
+ rangePositions.get(id).endIndex = positionIndex;
28198
+ }
28199
+ recentlyClosedComments.add(id);
28200
+ }
28201
+ lastElementWasCommentMarker = true;
28202
+ } else if (isTrackedChange) {
28203
+ const trackedChangeId = element.attributes?.["w:id"];
28204
+ const author = element.attributes?.["w:author"];
28205
+ const date = element.attributes?.["w:date"];
28206
+ const elementType = element.name;
28207
+ let mappedId = trackedChangeId;
28208
+ let isReplacement = false;
28209
+ if (trackedChangeId !== void 0 && converter) {
28210
+ if (!converter.trackedChangeIdMap) {
28211
+ converter.trackedChangeIdMap = /* @__PURE__ */ new Map();
28212
+ }
28213
+ if (lastTrackedChange && lastTrackedChange.type !== elementType && lastTrackedChange.author === author && lastTrackedChange.date === date) {
28214
+ mappedId = lastTrackedChange.mappedId;
28215
+ converter.trackedChangeIdMap.set(String(trackedChangeId), mappedId);
28216
+ isReplacement = true;
28217
+ } else {
28218
+ if (!converter.trackedChangeIdMap.has(String(trackedChangeId))) {
28219
+ converter.trackedChangeIdMap.set(String(trackedChangeId), uuid.v4());
28220
+ }
28221
+ mappedId = converter.trackedChangeIdMap.get(String(trackedChangeId));
28222
+ }
28223
+ }
28224
+ if (currentTrackedChangeId === null) {
28225
+ if (isReplacement) {
28226
+ lastTrackedChange = null;
28227
+ } else {
28228
+ lastTrackedChange = {
28229
+ type: elementType,
28230
+ author,
28231
+ date,
28232
+ mappedId,
28233
+ wordId: String(trackedChangeId)
28234
+ };
28235
+ }
28236
+ }
28237
+ if (mappedId && recentlyClosedComments.size > 0) {
28238
+ recentlyClosedComments.forEach((commentId) => {
28239
+ if (!commentsInTrackedChanges.has(commentId)) {
28240
+ commentsInTrackedChanges.set(commentId, String(mappedId));
28241
+ }
28242
+ });
28243
+ }
28244
+ recentlyClosedComments.clear();
28245
+ if (element.elements && Array.isArray(element.elements)) {
28246
+ walkElements(element.elements, mappedId !== void 0 ? String(mappedId) : currentTrackedChangeId);
28247
+ }
28248
+ } else {
28249
+ if (lastElementWasCommentMarker) {
28250
+ positionIndex++;
28251
+ lastElementWasCommentMarker = false;
28252
+ }
28253
+ if (element.name === "w:p") {
28254
+ recentlyClosedComments.clear();
28255
+ lastTrackedChange = null;
28256
+ }
28257
+ if (element.elements && Array.isArray(element.elements)) {
28258
+ walkElements(element.elements, currentTrackedChangeId);
28142
28259
  }
28143
- }
28144
- if (element.elements && Array.isArray(element.elements)) {
28145
- walkElements(element.elements);
28146
28260
  }
28147
28261
  });
28148
28262
  };
@@ -28152,19 +28266,20 @@ const extractCommentRangesFromDocument = (docx) => {
28152
28266
  walkElements(body.elements);
28153
28267
  }
28154
28268
  }
28155
- return pendingComments;
28269
+ return { rangeEvents, rangePositions, commentsInTrackedChanges };
28156
28270
  };
28157
- const detectThreadingFromRanges = (comments, rangeEvents) => {
28158
- if (!rangeEvents || rangeEvents.length === 0) {
28159
- return comments;
28160
- }
28271
+ const detectThreadingFromNestedRanges = (comments, rangeEvents, skipComments = /* @__PURE__ */ new Set()) => {
28161
28272
  const openRanges = [];
28162
28273
  const parentMap = /* @__PURE__ */ new Map();
28163
28274
  rangeEvents.forEach((event) => {
28164
28275
  if (event.type === "start") {
28165
- if (openRanges.length > 0) {
28166
- const parentCommentId = openRanges[openRanges.length - 1];
28167
- parentMap.set(event.commentId, parentCommentId);
28276
+ if (!skipComments.has(event.commentId) && openRanges.length > 0) {
28277
+ for (let i = openRanges.length - 1; i >= 0; i--) {
28278
+ if (!skipComments.has(openRanges[i])) {
28279
+ parentMap.set(event.commentId, openRanges[i]);
28280
+ break;
28281
+ }
28282
+ }
28168
28283
  }
28169
28284
  openRanges.push(event.commentId);
28170
28285
  } else if (event.type === "end") {
@@ -28174,10 +28289,113 @@ const detectThreadingFromRanges = (comments, rangeEvents) => {
28174
28289
  }
28175
28290
  }
28176
28291
  });
28292
+ return parentMap;
28293
+ };
28294
+ const detectThreadingFromSharedPosition = (comments, rangePositions) => {
28295
+ const parentMap = /* @__PURE__ */ new Map();
28296
+ const commentsByStartPosition = /* @__PURE__ */ new Map();
28297
+ comments.forEach((comment) => {
28298
+ const position = rangePositions.get(comment.importedId);
28299
+ if (position && position.startIndex >= 0) {
28300
+ const startKey = position.startIndex;
28301
+ if (!commentsByStartPosition.has(startKey)) {
28302
+ commentsByStartPosition.set(startKey, []);
28303
+ }
28304
+ commentsByStartPosition.get(startKey).push(comment);
28305
+ }
28306
+ });
28307
+ commentsByStartPosition.forEach((commentsAtPosition) => {
28308
+ if (commentsAtPosition.length <= 1) return;
28309
+ const sorted = [...commentsAtPosition].sort((a, b2) => a.createdTime - b2.createdTime);
28310
+ const parentComment = sorted[0];
28311
+ for (let i = 1; i < sorted.length; i++) {
28312
+ parentMap.set(sorted[i].importedId, parentComment.importedId);
28313
+ }
28314
+ });
28315
+ return parentMap;
28316
+ };
28317
+ const detectThreadingFromMissingRanges = (comments, rangePositions) => {
28318
+ const parentMap = /* @__PURE__ */ new Map();
28319
+ const commentsWithRanges = [];
28320
+ const commentsWithoutRanges = [];
28321
+ comments.forEach((comment) => {
28322
+ const position = rangePositions.get(comment.importedId);
28323
+ if (position && position.startIndex >= 0) {
28324
+ commentsWithRanges.push(comment);
28325
+ } else {
28326
+ commentsWithoutRanges.push(comment);
28327
+ }
28328
+ });
28329
+ commentsWithoutRanges.forEach((comment) => {
28330
+ const potentialParents = commentsWithRanges.filter((c) => c.createdTime < comment.createdTime).sort((a, b2) => b2.createdTime - a.createdTime);
28331
+ if (potentialParents.length > 0) {
28332
+ parentMap.set(comment.importedId, potentialParents[0].importedId);
28333
+ }
28334
+ });
28335
+ return parentMap;
28336
+ };
28337
+ const detectThreadingFromTrackedChanges = (comments, commentsInTrackedChanges) => {
28338
+ const parentMap = /* @__PURE__ */ new Map();
28339
+ if (!commentsInTrackedChanges || commentsInTrackedChanges.size === 0) {
28340
+ return parentMap;
28341
+ }
28342
+ comments.forEach((comment) => {
28343
+ const trackedChangeId = commentsInTrackedChanges.get(comment.importedId);
28344
+ if (trackedChangeId !== void 0) {
28345
+ parentMap.set(comment.importedId, { trackedChangeId, isTrackedChangeParent: true });
28346
+ }
28347
+ });
28348
+ return parentMap;
28349
+ };
28350
+ const detectThreadingFromRanges = (comments, rangeData) => {
28351
+ const { rangeEvents, rangePositions, commentsInTrackedChanges } = Array.isArray(rangeData) ? { rangeEvents: rangeData, rangePositions: /* @__PURE__ */ new Map(), commentsInTrackedChanges: /* @__PURE__ */ new Map() } : rangeData;
28352
+ if (!rangeEvents || rangeEvents.length === 0) {
28353
+ if (comments.length > 1) {
28354
+ const parentMap = detectThreadingFromMissingRanges(comments, rangePositions);
28355
+ return applyParentRelationships(comments, parentMap);
28356
+ }
28357
+ return comments;
28358
+ }
28359
+ const commentsWithSharedPosition = findCommentsWithSharedStartPosition(comments, rangePositions);
28360
+ const nestedParentMap = detectThreadingFromNestedRanges(comments, rangeEvents, commentsWithSharedPosition);
28361
+ const sharedPositionParentMap = detectThreadingFromSharedPosition(comments, rangePositions);
28362
+ const missingRangeParentMap = detectThreadingFromMissingRanges(comments, rangePositions);
28363
+ const trackedChangeParentMap = detectThreadingFromTrackedChanges(comments, commentsInTrackedChanges);
28364
+ const mergedParentMap = new Map([...missingRangeParentMap, ...nestedParentMap, ...sharedPositionParentMap]);
28365
+ return applyParentRelationships(comments, mergedParentMap, trackedChangeParentMap);
28366
+ };
28367
+ const findCommentsWithSharedStartPosition = (comments, rangePositions) => {
28368
+ const sharedPositionComments = /* @__PURE__ */ new Set();
28369
+ const commentsByStartPosition = /* @__PURE__ */ new Map();
28370
+ comments.forEach((comment) => {
28371
+ const position = rangePositions.get(comment.importedId);
28372
+ if (position && position.startIndex >= 0) {
28373
+ const startKey = position.startIndex;
28374
+ if (!commentsByStartPosition.has(startKey)) {
28375
+ commentsByStartPosition.set(startKey, []);
28376
+ }
28377
+ commentsByStartPosition.get(startKey).push(comment.importedId);
28378
+ }
28379
+ });
28380
+ commentsByStartPosition.forEach((commentIds) => {
28381
+ if (commentIds.length > 1) {
28382
+ commentIds.forEach((id) => sharedPositionComments.add(id));
28383
+ }
28384
+ });
28385
+ return sharedPositionComments;
28386
+ };
28387
+ const applyParentRelationships = (comments, parentMap, trackedChangeParentMap = /* @__PURE__ */ new Map()) => {
28177
28388
  return comments.map((comment) => {
28178
- const parentCommentId = parentMap.get(comment.importedId);
28179
- if (parentCommentId) {
28180
- const parentComment = comments.find((c) => c.importedId === parentCommentId);
28389
+ const trackedChangeParent = trackedChangeParentMap.get(comment.importedId);
28390
+ if (trackedChangeParent && trackedChangeParent.isTrackedChangeParent) {
28391
+ return {
28392
+ ...comment,
28393
+ parentCommentId: trackedChangeParent.trackedChangeId
28394
+ };
28395
+ }
28396
+ const parentImportedId = parentMap.get(comment.importedId);
28397
+ if (parentImportedId) {
28398
+ const parentComment = comments.find((c) => c.importedId === parentImportedId);
28181
28399
  if (parentComment) {
28182
28400
  return {
28183
28401
  ...comment,
@@ -29053,9 +29271,30 @@ const commentRangeEndHandlerEntity = generateV2HandlerEntity(
29053
29271
  );
29054
29272
  const permStartHandlerEntity = generateV2HandlerEntity("permStartHandler", translator$8);
29055
29273
  const permEndHandlerEntity = generateV2HandlerEntity("permEndHandler", translator$7);
29274
+ const detectDocumentOrigin = (docx) => {
29275
+ const commentsExtended = docx["word/commentsExtended.xml"];
29276
+ if (commentsExtended) {
29277
+ const { elements: initialElements = [] } = commentsExtended;
29278
+ if (initialElements?.length > 0) {
29279
+ const { elements = [] } = initialElements[0] ?? {};
29280
+ const commentEx = elements.filter((el) => el.name === "w15:commentEx");
29281
+ if (commentEx.length > 0) {
29282
+ return "word";
29283
+ }
29284
+ }
29285
+ }
29286
+ const comments = docx["word/comments.xml"];
29287
+ if (comments && !commentsExtended) {
29288
+ return "google-docs";
29289
+ }
29290
+ return "unknown";
29291
+ };
29056
29292
  const createDocumentJson = (docx, converter, editor) => {
29057
29293
  const json = carbonCopy(getInitialJSON(docx));
29058
29294
  if (!json) return null;
29295
+ if (converter) {
29296
+ converter.documentOrigin = detectDocumentOrigin(docx);
29297
+ }
29059
29298
  if (converter?.telemetry) {
29060
29299
  const files = Object.keys(docx).map((filePath) => {
29061
29300
  const parts = filePath.split("/");
@@ -30627,7 +30866,9 @@ const getCommentDefinition = (comment, commentId, allComments, editor) => {
30627
30866
  };
30628
30867
  if (comment?.parentCommentId) {
30629
30868
  const parentComment = allComments.find((c) => c.commentId === comment.parentCommentId);
30630
- attributes["w15:paraIdParent"] = parentComment.commentParaId;
30869
+ if (parentComment && !parentComment.trackedChange) {
30870
+ attributes["w15:paraIdParent"] = parentComment.commentParaId;
30871
+ }
30631
30872
  }
30632
30873
  return {
30633
30874
  type: "element",
@@ -30672,7 +30913,22 @@ const updateCommentsXml = (commentDefs = [], commentsXml) => {
30672
30913
  newCommentsXml.elements[0].elements = commentDefs;
30673
30914
  return newCommentsXml;
30674
30915
  };
30675
- const updateCommentsExtendedXml = (comments = [], commentsExtendedXml) => {
30916
+ const determineExportStrategy = (comments) => {
30917
+ if (!comments || comments.length === 0) {
30918
+ return "word";
30919
+ }
30920
+ const origins = new Set(comments.map((c) => c.origin || "word"));
30921
+ if (origins.size === 1) {
30922
+ const origin = origins.values().next().value;
30923
+ return origin === "google-docs" ? "google-docs" : "word";
30924
+ }
30925
+ return "word";
30926
+ };
30927
+ const updateCommentsExtendedXml = (comments = [], commentsExtendedXml, exportStrategy = "word") => {
30928
+ const shouldGenerateCommentsExtended = exportStrategy === "word" || comments.some((c) => c.originalXmlStructure?.hasCommentsExtended);
30929
+ if (!shouldGenerateCommentsExtended && exportStrategy === "google-docs") {
30930
+ return null;
30931
+ }
30676
30932
  const xmlCopy = carbonCopy(commentsExtendedXml);
30677
30933
  const commentsEx = comments.map((comment) => {
30678
30934
  const isResolved = comment.resolvedTime || comment.isDone;
@@ -30681,9 +30937,11 @@ const updateCommentsExtendedXml = (comments = [], commentsExtendedXml) => {
30681
30937
  "w15:done": isResolved ? "1" : "0"
30682
30938
  };
30683
30939
  const parentId = comment.parentCommentId;
30684
- if (parentId) {
30940
+ if (parentId && (exportStrategy === "word" || comment.originalXmlStructure?.hasCommentsExtended)) {
30685
30941
  const parentComment = comments.find((c) => c.commentId === parentId);
30686
- attributes["w15:paraIdParent"] = parentComment.commentParaId;
30942
+ if (parentComment && !parentComment.trackedChange) {
30943
+ attributes["w15:paraIdParent"] = parentComment.commentParaId;
30944
+ }
30687
30945
  }
30688
30946
  return {
30689
30947
  type: "element",
@@ -30753,14 +31011,21 @@ const prepareCommentsXmlFilesForExport = ({ convertedXml, defs, commentsWithPara
30753
31011
  const documentXml = removeCommentsFilesFromConvertedXml(convertedXml);
30754
31012
  return { documentXml, relationships };
30755
31013
  }
31014
+ const exportStrategy = determineExportStrategy(commentsWithParaIds);
30756
31015
  const updatedXml = generateConvertedXmlWithCommentFiles(convertedXml);
30757
31016
  updatedXml["word/comments.xml"] = updateCommentsXml(defs, updatedXml["word/comments.xml"]);
30758
31017
  relationships.push(generateRelationship("comments.xml"));
30759
- updatedXml["word/commentsExtended.xml"] = updateCommentsExtendedXml(
31018
+ const commentsExtendedXml = updateCommentsExtendedXml(
30760
31019
  commentsWithParaIds,
30761
- updatedXml["word/commentsExtended.xml"]
31020
+ updatedXml["word/commentsExtended.xml"],
31021
+ exportStrategy
30762
31022
  );
30763
- relationships.push(generateRelationship("commentsExtended.xml"));
31023
+ if (commentsExtendedXml !== null) {
31024
+ updatedXml["word/commentsExtended.xml"] = commentsExtendedXml;
31025
+ relationships.push(generateRelationship("commentsExtended.xml"));
31026
+ } else {
31027
+ delete updatedXml["word/commentsExtended.xml"];
31028
+ }
30764
31029
  const { documentIdsUpdated, extensibleUpdated } = updateCommentsIdsAndExtensible(
30765
31030
  commentsWithParaIds,
30766
31031
  updatedXml["word/commentsIds.xml"],
@@ -31269,7 +31534,7 @@ class SuperConverter {
31269
31534
  static getStoredSuperdocVersion(docx) {
31270
31535
  return SuperConverter.getStoredCustomProperty(docx, "SuperdocVersion");
31271
31536
  }
31272
- static setStoredSuperdocVersion(docx = this.convertedXml, version = "1.4.0") {
31537
+ static setStoredSuperdocVersion(docx = this.convertedXml, version = "1.4.1-next.1") {
31273
31538
  return SuperConverter.setStoredCustomProperty(docx, "SuperdocVersion", version, false);
31274
31539
  }
31275
31540
  /**
@@ -1,5 +1,5 @@
1
- import { B as BIT8, M as MAX_SAFE_INTEGER, c as create, a as BITS7, u as utf8TextDecoder, b as create$1, s as setIfUndefined, d as create$2, f as from, e as floor$1, g as equalityDeep, w as writeVarUint, h as writeVarString, t as toUint8Array, i as createEncoder, j as createInjectionKey, k as toString, l as throwError, m as useSsrAdapter, n as configProviderInjectionKey, o as cssrAnchorMetaName, p as globalStyle, q as cB, r as c, v as isMounted, x as commonVariables$2, y as cM, z as cNotM, A as cE, C as derived, D as changeColor, E as insideModal, F as insidePopover, G as resolveWrappedSlot, H as on, I as warnOnce, J as useConfig, K as useMergedState, L as useMemo, N as useTheme, O as useRtl, P as createKey, Q as useThemeClass, R as createId, S as call, T as render, U as messageProviderInjectionKey, V as messageApiInjectionKey, W as fromBase64, X as onChange, Y as varStorage, Z as toBase64, _ as createUint8ArrayFromArrayBuffer, $ as offChange, a0 as writeVarUint8Array, a1 as map, a2 as length, a3 as isNode, a4 as min, a5 as pow, a6 as comments_module_events, a7 as getFileObject, a8 as getTrackChanges, a9 as CommentsPluginKey, aa as TrackChangesBasePluginKey, ab as Editor, ac as getRichTextExtensions, ad as ellipsisVerticalSvg, ae as xmarkIconSvg, af as checkIconSvg, ag as caretDownIconSvg, ah as commentIconSvg, ai as _export_sfc, aj as NDropdown, ak as SuperInput, al as vClickOutside, am as PresentationEditor, an as SuperEditor, ao as AIWriter, ap as NConfigProvider, aq as SuperToolbar } from "./index-B6VX2S0H.es.js";
2
- import "./SuperConverter-DAO7c_d4.es.js";
1
+ import { B as BIT8, M as MAX_SAFE_INTEGER, c as create, a as BITS7, u as utf8TextDecoder, b as create$1, s as setIfUndefined, d as create$2, f as from, e as floor$1, g as equalityDeep, w as writeVarUint, h as writeVarString, t as toUint8Array, i as createEncoder, j as createInjectionKey, k as toString, l as throwError, m as useSsrAdapter, n as configProviderInjectionKey, o as cssrAnchorMetaName, p as globalStyle, q as cB, r as c, v as isMounted, x as commonVariables$2, y as cM, z as cNotM, A as cE, C as derived, D as changeColor, E as insideModal, F as insidePopover, G as resolveWrappedSlot, H as on, I as warnOnce, J as useConfig, K as useMergedState, L as useMemo, N as useTheme, O as useRtl, P as createKey, Q as useThemeClass, R as createId, S as call, T as render, U as messageProviderInjectionKey, V as messageApiInjectionKey, W as fromBase64, X as onChange, Y as varStorage, Z as toBase64, _ as createUint8ArrayFromArrayBuffer, $ as offChange, a0 as writeVarUint8Array, a1 as map, a2 as length, a3 as isNode, a4 as min, a5 as pow, a6 as comments_module_events, a7 as getFileObject, a8 as getTrackChanges, a9 as CommentsPluginKey, aa as TrackChangesBasePluginKey, ab as Editor, ac as getRichTextExtensions, ad as ellipsisVerticalSvg, ae as xmarkIconSvg, af as checkIconSvg, ag as caretDownIconSvg, ah as commentIconSvg, ai as _export_sfc, aj as NDropdown, ak as SuperInput, al as vClickOutside, am as PresentationEditor, an as SuperEditor, ao as AIWriter, ap as NConfigProvider, aq as SuperToolbar } from "./index-DwUAu2hL.es.js";
2
+ import "./SuperConverter-BuK6B5Kx.es.js";
3
3
  import { B as BlankDOCX } from "./blank-docx-ABm6XYAA.es.js";
4
4
  import { E as EventEmitter } from "./eventemitter3-CwrdEv8r.es.js";
5
5
  import { HocuspocusProvider, HocuspocusProviderWebsocket } from "@hocuspocus/provider";
@@ -5113,11 +5113,14 @@ const groupChanges = (changes) => {
5113
5113
  trackFormat: "formatMark"
5114
5114
  };
5115
5115
  const grouped = [];
5116
+ const processed = /* @__PURE__ */ new Set();
5116
5117
  for (let i = 0; i < changes.length; i++) {
5118
+ if (processed.has(i)) continue;
5117
5119
  const c1 = changes[i];
5118
- const c2 = changes[i + 1];
5119
5120
  const c1Key = markMetaKeys[c1.mark.type.name];
5120
- if (c1 && c2 && c1.to === c2.from && c1.mark.attrs.id === c2.mark.attrs.id) {
5121
+ const c1Id = c1.mark.attrs.id;
5122
+ const c2 = changes[i + 1];
5123
+ if (c2 && c1.to === c2.from && c1Id === c2.mark.attrs.id) {
5121
5124
  const c2Key = markMetaKeys[c2.mark.type.name];
5122
5125
  grouped.push({
5123
5126
  from: c1.from,
@@ -5125,13 +5128,35 @@ const groupChanges = (changes) => {
5125
5128
  [c1Key]: c1,
5126
5129
  [c2Key]: c2
5127
5130
  });
5128
- i++;
5129
- } else {
5131
+ processed.add(i);
5132
+ processed.add(i + 1);
5133
+ continue;
5134
+ }
5135
+ let foundMatch = false;
5136
+ for (let j = i + 1; j < changes.length; j++) {
5137
+ if (processed.has(j)) continue;
5138
+ const c22 = changes[j];
5139
+ if (c1Id === c22.mark.attrs.id && c1.mark.type.name !== c22.mark.type.name) {
5140
+ const c2Key = markMetaKeys[c22.mark.type.name];
5141
+ grouped.push({
5142
+ from: Math.min(c1.from, c22.from),
5143
+ to: Math.max(c1.to, c22.to),
5144
+ [c1Key]: c1,
5145
+ [c2Key]: c22
5146
+ });
5147
+ processed.add(i);
5148
+ processed.add(j);
5149
+ foundMatch = true;
5150
+ break;
5151
+ }
5152
+ }
5153
+ if (!foundMatch) {
5130
5154
  grouped.push({
5131
5155
  from: c1.from,
5132
5156
  to: c1.to,
5133
5157
  [c1Key]: c1
5134
5158
  });
5159
+ processed.add(i);
5135
5160
  }
5136
5161
  }
5137
5162
  return grouped;
@@ -5433,7 +5458,12 @@ const useCommentsStore = /* @__PURE__ */ defineStore("comments", () => {
5433
5458
  trackedChange: comment.trackedChange || false,
5434
5459
  trackedChangeText: comment.trackedChangeText,
5435
5460
  trackedChangeType: comment.trackedChangeType,
5436
- deletedText: comment.trackedDeletedText
5461
+ deletedText: comment.trackedDeletedText,
5462
+ // Preserve origin metadata for export
5463
+ origin: comment.origin || "word",
5464
+ // Default to 'word' for backward compatibility
5465
+ threadingMethod: comment.threadingMethod,
5466
+ originalXmlStructure: comment.originalXmlStructure
5437
5467
  });
5438
5468
  addComment({ superdoc, comment: newComment });
5439
5469
  });
@@ -6213,7 +6243,11 @@ const _sfc_main$c = {
6213
6243
  const isThreadedComment = c2.parentCommentId === parentComment.commentId;
6214
6244
  const isThisComment = c2.commentId === props.comment.commentId;
6215
6245
  return isThreadedComment || isThisComment;
6216
- }).sort((a, b) => a.commentId === props.comment.commentId && a.createdTime - b.createdTime);
6246
+ }).sort((a, b) => {
6247
+ if (a.commentId === props.comment.commentId) return -1;
6248
+ if (b.commentId === props.comment.commentId) return 1;
6249
+ return a.createdTime - b.createdTime;
6250
+ });
6217
6251
  });
6218
6252
  const isInternalDropdownDisabled = computed(() => {
6219
6253
  if (props.comment.resolvedTime) return true;
@@ -6472,7 +6506,7 @@ const _sfc_main$c = {
6472
6506
  };
6473
6507
  }
6474
6508
  };
6475
- const CommentDialog = /* @__PURE__ */ _export_sfc(_sfc_main$c, [["__scopeId", "data-v-d50fd675"]]);
6509
+ const CommentDialog = /* @__PURE__ */ _export_sfc(_sfc_main$c, [["__scopeId", "data-v-f9956635"]]);
6476
6510
  const _hoisted_1$a = { class: "comments-list" };
6477
6511
  const _hoisted_2$5 = { key: 0 };
6478
6512
  const _hoisted_3$3 = { class: "comment-item" };
@@ -7479,7 +7513,7 @@ const _sfc_main = {
7479
7513
  __name: "SuperDoc",
7480
7514
  emits: ["selection-update"],
7481
7515
  setup(__props, { emit: __emit }) {
7482
- const PdfViewer = defineAsyncComponent(() => import("./PdfViewer-CihMmpz1.es.js"));
7516
+ const PdfViewer = defineAsyncComponent(() => import("./PdfViewer-D_1Yi2Kj.es.js"));
7483
7517
  const superdocStore = useSuperdocStore();
7484
7518
  const commentsStore = useCommentsStore();
7485
7519
  const {
@@ -8433,7 +8467,7 @@ class SuperDoc extends EventEmitter {
8433
8467
  this.config.colors = shuffleArray(this.config.colors);
8434
8468
  this.userColorMap = /* @__PURE__ */ new Map();
8435
8469
  this.colorIndex = 0;
8436
- this.version = "1.4.0";
8470
+ this.version = "1.4.1-next.1";
8437
8471
  this.#log("🦋 [superdoc] Using SuperDoc version:", this.version);
8438
8472
  this.superdocId = config.superdocId || v4();
8439
8473
  this.colors = this.config.colors;