@ctzhian/tiptap 2.1.12 → 2.1.14

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,30 +1,30 @@
1
- function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); }
2
- function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
3
- function _iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (null != t) { var e, n, i, u, a = [], f = !0, o = !1; try { if (i = (t = t.call(r)).next, 0 === l) { if (Object(t) !== t) return; f = !1; } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); } catch (r) { o = !0, n = r; } finally { try { if (!f && null != t.return && (u = t.return(), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } }
4
- function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
5
1
  function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); }
6
2
  function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
7
3
  function _iterableToArray(iter) { if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter); }
8
4
  function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) return _arrayLikeToArray(arr); }
5
+ function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); }
6
+ function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
7
+ function _iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (null != t) { var e, n, i, u, a = [], f = !0, o = !1; try { if (i = (t = t.call(r)).next, 0 === l) { if (Object(t) !== t) return; f = !1; } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); } catch (r) { o = !0, n = r; } finally { try { if (!f && null != t.return && (u = t.return(), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } }
8
+ function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
9
9
  function _createForOfIteratorHelper(o, allowArrayLike) { var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"]; if (!it) { if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = it.call(o); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it.return != null) it.return(); } finally { if (didErr) throw err; } } }; }
10
10
  function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
11
11
  function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; }
12
12
  import { generateJSON } from '@tiptap/html';
13
13
  import DiffMatchPatch from 'diff-match-patch';
14
-
15
- // 创建diff-match-patch实例
16
14
  var dmp = new DiffMatchPatch();
17
-
18
- // 类型定义
19
-
20
- /**
21
- * 将HTML转换为ProseMirror文档结构
22
- * @param {string} html - HTML字符串
23
- * @param {Array} extensions - Tiptap扩展数组
24
- * @returns {Object} ProseMirror文档对象
25
- */
26
15
  export function parseHtmlToDoc(html, extensions) {
27
- return generateJSON(html, extensions);
16
+ try {
17
+ if (!html || typeof html !== 'string') {
18
+ throw new Error('HTML 内容无效');
19
+ }
20
+ if (!extensions || extensions.length === 0) {
21
+ throw new Error('扩展数组不能为空');
22
+ }
23
+ return generateJSON(html, extensions);
24
+ } catch (error) {
25
+ console.error('解析 HTML 到文档结构时出错:', error);
26
+ throw error;
27
+ }
28
28
  }
29
29
  function haveSameMarks(a, b) {
30
30
  var arrA = a || [];
@@ -48,191 +48,6 @@ function haveSameMarks(a, b) {
48
48
  }
49
49
  return true;
50
50
  }
51
-
52
- /**
53
- * 对比两个ProseMirror文档节点
54
- * @param {Object} nodeA - 旧文档节点
55
- * @param {Object} nodeB - 新文档节点
56
- * @param {Array} path - 当前节点路径
57
- * @returns {Array} 差异数组
58
- */
59
- function compareNodes(nodeA, nodeB) {
60
- var path = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : [];
61
- var diffs = [];
62
-
63
- // 如果节点类型不同,标记整个节点为变更
64
- if ((nodeA === null || nodeA === void 0 ? void 0 : nodeA.type) !== (nodeB === null || nodeB === void 0 ? void 0 : nodeB.type)) {
65
- if (nodeA) {
66
- diffs.push({
67
- type: 'delete',
68
- path: _toConsumableArray(path),
69
- node: nodeA
70
- });
71
- }
72
- if (nodeB) {
73
- diffs.push({
74
- type: 'insert',
75
- path: _toConsumableArray(path),
76
- node: nodeB
77
- });
78
- }
79
- return diffs;
80
- }
81
-
82
- // 如果是文本节点,进行文本级别的diff
83
- if ((nodeA === null || nodeA === void 0 ? void 0 : nodeA.type) === 'text' && (nodeB === null || nodeB === void 0 ? void 0 : nodeB.type) === 'text') {
84
- if (nodeA.text !== nodeB.text) {
85
- // 计算文本差异
86
- var textDiffs = dmp.diff_main(nodeA.text || '', nodeB.text || '');
87
- dmp.diff_cleanupSemantic(textDiffs);
88
-
89
- // 检查是否有文本差异
90
- // if (textDiffs.length > 1) {
91
- // console.log('发现文本差异:', { oldText: nodeA.text, newText: nodeB.text, path });
92
- // }
93
-
94
- var textOffset = 0; // offset in the NEW text node
95
-
96
- textDiffs.forEach(function (_ref) {
97
- var _ref2 = _slicedToArray(_ref, 2),
98
- operation = _ref2[0],
99
- text = _ref2[1];
100
- if (operation === -1) {
101
- // Delete
102
- // The widget should be placed at the current position in the new text.
103
- var diffItem = {
104
- type: 'delete',
105
- path: _toConsumableArray(path),
106
- textDiff: {
107
- offset: textOffset,
108
- length: text.length,
109
- text: text,
110
- operation: operation
111
- }
112
- };
113
- diffs.push(diffItem);
114
- // DO NOT advance textOffset
115
- } else if (operation === 1) {
116
- // Insert
117
- var _diffItem = {
118
- type: 'insert',
119
- path: _toConsumableArray(path),
120
- textDiff: {
121
- offset: textOffset,
122
- length: text.length,
123
- text: text,
124
- operation: operation
125
- }
126
- };
127
- diffs.push(_diffItem);
128
- textOffset += text.length; // DO advance textOffset
129
- } else {
130
- // Equal
131
- textOffset += text.length; // DO advance textOffset
132
- }
133
- });
134
- }
135
- // 文本相等或不相等时都可检查 marks 差异(粗粒度:节点级)
136
- if (!haveSameMarks(nodeA.marks, nodeB.marks)) {
137
- diffs.push({
138
- type: 'modify',
139
- path: _toConsumableArray(path),
140
- attrChange: {
141
- key: 'marks',
142
- oldValue: nodeA.marks || [],
143
- newValue: nodeB.marks || []
144
- }
145
- });
146
- }
147
- return diffs;
148
- }
149
-
150
- // 优先:段落/标题等内联容器执行字符级 diff 和 marks 区间对比
151
- if (nodeA && nodeB && isInlineContainer(nodeA) && isInlineContainer(nodeB)) {
152
- // 文本与 marks 的字符级 diff
153
- diffs.push.apply(diffs, _toConsumableArray(compareInlineContainer(nodeA, nodeB, path)));
154
- // 非文本内联节点(如行内数学、行内公式等)的结构化对比
155
- diffs.push.apply(diffs, _toConsumableArray(compareInlineContainerChildren(nodeA, nodeB, path)));
156
- return diffs;
157
- }
158
-
159
- // 对比节点属性
160
- var attrsA = (nodeA === null || nodeA === void 0 ? void 0 : nodeA.attrs) || {};
161
- var attrsB = (nodeB === null || nodeB === void 0 ? void 0 : nodeB.attrs) || {};
162
- var allAttrKeys = new Set([].concat(_toConsumableArray(Object.keys(attrsA)), _toConsumableArray(Object.keys(attrsB))));
163
- for (var _i = 0, _Array$from = Array.from(allAttrKeys); _i < _Array$from.length; _i++) {
164
- var key = _Array$from[_i];
165
- if (attrsA[key] !== attrsB[key]) {
166
- diffs.push({
167
- type: 'modify',
168
- path: _toConsumableArray(path),
169
- attrChange: {
170
- key: key,
171
- oldValue: attrsA[key],
172
- newValue: attrsB[key]
173
- }
174
- });
175
- }
176
- }
177
-
178
- // 使用 LCS 对齐子节点,减少错配
179
- var contentA = (nodeA === null || nodeA === void 0 ? void 0 : nodeA.content) || [];
180
- var contentB = (nodeB === null || nodeB === void 0 ? void 0 : nodeB.content) || [];
181
- var pairs = lcsAlign(contentA, contentB, nodesEqualForAlign);
182
- var ai = 0;
183
- var bi = 0;
184
- for (var _i2 = 0, _pairs = pairs; _i2 < _pairs.length; _i2++) {
185
- var _pairs$_i = _slicedToArray(_pairs[_i2], 2),
186
- i = _pairs$_i[0],
187
- j = _pairs$_i[1];
188
- // 先处理 A 中未匹配(删除),以 B 的当前位置作为锚点
189
- while (ai < i) {
190
- var delNode = contentA[ai];
191
- diffs.push({
192
- type: 'delete',
193
- path: [].concat(_toConsumableArray(path), [bi]),
194
- node: delNode
195
- });
196
- ai++;
197
- }
198
- // 再处理 B 中未匹配(插入)
199
- while (bi < j) {
200
- var insNode = contentB[bi];
201
- diffs.push({
202
- type: 'insert',
203
- path: [].concat(_toConsumableArray(path), [bi]),
204
- node: insNode
205
- });
206
- bi++;
207
- }
208
- // 匹配上的成对递归,路径以新文档索引为准
209
- var childA = contentA[i];
210
- var childB = contentB[j];
211
- diffs.push.apply(diffs, _toConsumableArray(compareNodes(childA, childB, [].concat(_toConsumableArray(path), [j]))));
212
- ai = i + 1;
213
- bi = j + 1;
214
- }
215
- // 处理尾部剩余未匹配项
216
- while (ai < contentA.length) {
217
- var _delNode = contentA[ai];
218
- diffs.push({
219
- type: 'delete',
220
- path: [].concat(_toConsumableArray(path), [bi]),
221
- node: _delNode
222
- });
223
- ai++;
224
- }
225
- while (bi < contentB.length) {
226
- var _insNode = contentB[bi];
227
- diffs.push({
228
- type: 'insert',
229
- path: [].concat(_toConsumableArray(path), [bi]),
230
- node: _insNode
231
- });
232
- bi++;
233
- }
234
- return diffs;
235
- }
236
51
  function isInlineContainer(node) {
237
52
  return node.type === 'paragraph' || node.type === 'heading';
238
53
  }
@@ -275,13 +90,12 @@ function compareInlineContainer(nodeA, nodeB, path) {
275
90
  dmp.diff_cleanupSemantic(blocks);
276
91
  var oldOffset = 0;
277
92
  var newOffset = 0;
278
- for (var _i3 = 0, _arr = blocks; _i3 < _arr.length; _i3++) {
279
- var _arr$_i = _slicedToArray(_arr[_i3], 2),
93
+ for (var _i = 0, _arr = blocks; _i < _arr.length; _i++) {
94
+ var _arr$_i = _slicedToArray(_arr[_i], 2),
280
95
  operation = _arr$_i[0],
281
96
  text = _arr$_i[1];
282
97
  var len = text.length;
283
98
  if (operation === -1) {
284
- // 删除:在新文本当前位置放置删除widget
285
99
  diffs.push({
286
100
  type: 'delete',
287
101
  path: _toConsumableArray(path),
@@ -294,7 +108,6 @@ function compareInlineContainer(nodeA, nodeB, path) {
294
108
  });
295
109
  oldOffset += len;
296
110
  } else if (operation === 1) {
297
- // 插入:直接在新文本当前位置标注
298
111
  diffs.push({
299
112
  type: 'insert',
300
113
  path: _toConsumableArray(path),
@@ -307,7 +120,6 @@ function compareInlineContainer(nodeA, nodeB, path) {
307
120
  });
308
121
  newOffset += len;
309
122
  } else {
310
- // 相等:检测 marks 差异并生成区间级 modify
311
123
  var runStart = null;
312
124
  for (var i = 0; i < len; i++) {
313
125
  var oldIdx = oldOffset + i;
@@ -331,16 +143,16 @@ function compareInlineContainer(nodeA, nodeB, path) {
331
143
  }
332
144
  }
333
145
  if (runStart !== null) {
334
- var _i4 = len;
146
+ var _i2 = len;
335
147
  diffs.push({
336
148
  type: 'modify',
337
149
  path: _toConsumableArray(path),
338
150
  attrChange: {
339
151
  key: 'marks',
340
- oldValue: oldMarks.slice(oldOffset + runStart, oldOffset + _i4),
341
- newValue: newMarks.slice(newOffset + runStart, newOffset + _i4),
152
+ oldValue: oldMarks.slice(oldOffset + runStart, oldOffset + _i2),
153
+ newValue: newMarks.slice(newOffset + runStart, newOffset + _i2),
342
154
  fromOffset: newOffset + runStart,
343
- toOffset: newOffset + _i4
155
+ toOffset: newOffset + _i2
344
156
  }
345
157
  });
346
158
  }
@@ -350,31 +162,6 @@ function compareInlineContainer(nodeA, nodeB, path) {
350
162
  }
351
163
  return diffs;
352
164
  }
353
- function nodesEqualForAlign(a, b) {
354
- if (!a || !b) return false;
355
- if (a.type !== b.type) return false;
356
- // 对部分结构节点使用关键属性辅助匹配
357
- if (a.type === 'heading') {
358
- var _a$attrs$level, _a$attrs, _b$attrs$level, _b$attrs;
359
- return ((_a$attrs$level = (_a$attrs = a.attrs) === null || _a$attrs === void 0 ? void 0 : _a$attrs.level) !== null && _a$attrs$level !== void 0 ? _a$attrs$level : null) === ((_b$attrs$level = (_b$attrs = b.attrs) === null || _b$attrs === void 0 ? void 0 : _b$attrs.level) !== null && _b$attrs$level !== void 0 ? _b$attrs$level : null);
360
- }
361
- if (a.type === 'codeBlock' || a.type === 'code_block') {
362
- var _ref3, _a$attrs$language, _a$attrs2, _a$attrs3, _ref4, _b$attrs$language, _b$attrs2, _b$attrs3;
363
- var langA = (_ref3 = (_a$attrs$language = (_a$attrs2 = a.attrs) === null || _a$attrs2 === void 0 ? void 0 : _a$attrs2.language) !== null && _a$attrs$language !== void 0 ? _a$attrs$language : (_a$attrs3 = a.attrs) === null || _a$attrs3 === void 0 ? void 0 : _a$attrs3.lang) !== null && _ref3 !== void 0 ? _ref3 : null;
364
- var langB = (_ref4 = (_b$attrs$language = (_b$attrs2 = b.attrs) === null || _b$attrs2 === void 0 ? void 0 : _b$attrs2.language) !== null && _b$attrs$language !== void 0 ? _b$attrs$language : (_b$attrs3 = b.attrs) === null || _b$attrs3 === void 0 ? void 0 : _b$attrs3.lang) !== null && _ref4 !== void 0 ? _ref4 : null;
365
- return langA === langB;
366
- }
367
- if (a.type === 'table_cell' || a.type === 'tableHeader' || a.type === 'table_header') {
368
- var _a$attrs$colspan, _a$attrs4, _b$attrs$colspan, _b$attrs4, _a$attrs$rowspan, _a$attrs5, _b$attrs$rowspan, _b$attrs5;
369
- var colspanA = (_a$attrs$colspan = (_a$attrs4 = a.attrs) === null || _a$attrs4 === void 0 ? void 0 : _a$attrs4.colspan) !== null && _a$attrs$colspan !== void 0 ? _a$attrs$colspan : 1;
370
- var colspanB = (_b$attrs$colspan = (_b$attrs4 = b.attrs) === null || _b$attrs4 === void 0 ? void 0 : _b$attrs4.colspan) !== null && _b$attrs$colspan !== void 0 ? _b$attrs$colspan : 1;
371
- var rowspanA = (_a$attrs$rowspan = (_a$attrs5 = a.attrs) === null || _a$attrs5 === void 0 ? void 0 : _a$attrs5.rowspan) !== null && _a$attrs$rowspan !== void 0 ? _a$attrs$rowspan : 1;
372
- var rowspanB = (_b$attrs$rowspan = (_b$attrs5 = b.attrs) === null || _b$attrs5 === void 0 ? void 0 : _b$attrs5.rowspan) !== null && _b$attrs$rowspan !== void 0 ? _b$attrs$rowspan : 1;
373
- return colspanA === colspanB && rowspanA === rowspanB;
374
- }
375
- // 默认仅按类型
376
- return true;
377
- }
378
165
  function compareInlineContainerChildren(nodeA, nodeB, path) {
379
166
  var diffs = [];
380
167
  var aList = (nodeA.content || []).map(function (n, idx) {
@@ -395,17 +182,15 @@ function compareInlineContainerChildren(nodeA, nodeB, path) {
395
182
  });
396
183
  var equals = function equals(x, y) {
397
184
  if (!x || !y) return false;
398
- // 仅按类型对齐,属性差异作为 modify 处理
399
185
  return x.n.type === y.n.type;
400
186
  };
401
187
  var pairs = lcsAlign(aList, bList, equals);
402
188
  var ai = 0,
403
189
  bi = 0;
404
- for (var _i5 = 0, _pairs2 = pairs; _i5 < _pairs2.length; _i5++) {
405
- var _pairs2$_i = _slicedToArray(_pairs2[_i5], 2),
406
- i = _pairs2$_i[0],
407
- j = _pairs2$_i[1];
408
- // 删除:使用新文档当前位置作为锚点路径
190
+ for (var _i3 = 0, _pairs = pairs; _i3 < _pairs.length; _i3++) {
191
+ var _pairs$_i = _slicedToArray(_pairs[_i3], 2),
192
+ i = _pairs$_i[0],
193
+ j = _pairs$_i[1];
409
194
  while (ai < i) {
410
195
  var del = aList[ai];
411
196
  var anchorIdx = bi < bList.length ? bList[bi].idx : nodeB.content ? nodeB.content.length : 0;
@@ -416,7 +201,6 @@ function compareInlineContainerChildren(nodeA, nodeB, path) {
416
201
  });
417
202
  ai++;
418
203
  }
419
- // 插入:直接使用新文档该节点的实际索引
420
204
  while (bi < j) {
421
205
  var ins = bList[bi];
422
206
  diffs.push({
@@ -426,7 +210,6 @@ function compareInlineContainerChildren(nodeA, nodeB, path) {
426
210
  });
427
211
  bi++;
428
212
  }
429
- // modify:同类型节点进行属性对比,路径指向新文档该内联节点索引
430
213
  var aItem = aList[i];
431
214
  var bItem = bList[j];
432
215
  if (aItem && bItem) {
@@ -447,8 +230,6 @@ function compareInlineContainerChildren(nodeA, nodeB, path) {
447
230
  ai = i + 1;
448
231
  bi = j + 1;
449
232
  }
450
-
451
- // 尾部删除
452
233
  while (ai < aList.length) {
453
234
  var _del = aList[ai++];
454
235
  var _anchorIdx = bi < bList.length ? bList[bi].idx : nodeB.content ? nodeB.content.length : 0;
@@ -458,7 +239,6 @@ function compareInlineContainerChildren(nodeA, nodeB, path) {
458
239
  node: _del.n
459
240
  });
460
241
  }
461
- // 尾部插入
462
242
  while (bi < bList.length) {
463
243
  var _ins = bList[bi++];
464
244
  diffs.push({
@@ -469,6 +249,29 @@ function compareInlineContainerChildren(nodeA, nodeB, path) {
469
249
  }
470
250
  return diffs;
471
251
  }
252
+ function nodesEqualForAlign(a, b) {
253
+ if (!a || !b) return false;
254
+ if (a.type !== b.type) return false;
255
+ if (a.type === 'heading') {
256
+ var _a$attrs$level, _a$attrs, _b$attrs$level, _b$attrs;
257
+ return ((_a$attrs$level = (_a$attrs = a.attrs) === null || _a$attrs === void 0 ? void 0 : _a$attrs.level) !== null && _a$attrs$level !== void 0 ? _a$attrs$level : null) === ((_b$attrs$level = (_b$attrs = b.attrs) === null || _b$attrs === void 0 ? void 0 : _b$attrs.level) !== null && _b$attrs$level !== void 0 ? _b$attrs$level : null);
258
+ }
259
+ if (a.type === 'codeBlock' || a.type === 'code_block') {
260
+ var _ref, _a$attrs$language, _a$attrs2, _a$attrs3, _ref2, _b$attrs$language, _b$attrs2, _b$attrs3;
261
+ var langA = (_ref = (_a$attrs$language = (_a$attrs2 = a.attrs) === null || _a$attrs2 === void 0 ? void 0 : _a$attrs2.language) !== null && _a$attrs$language !== void 0 ? _a$attrs$language : (_a$attrs3 = a.attrs) === null || _a$attrs3 === void 0 ? void 0 : _a$attrs3.lang) !== null && _ref !== void 0 ? _ref : null;
262
+ var langB = (_ref2 = (_b$attrs$language = (_b$attrs2 = b.attrs) === null || _b$attrs2 === void 0 ? void 0 : _b$attrs2.language) !== null && _b$attrs$language !== void 0 ? _b$attrs$language : (_b$attrs3 = b.attrs) === null || _b$attrs3 === void 0 ? void 0 : _b$attrs3.lang) !== null && _ref2 !== void 0 ? _ref2 : null;
263
+ return langA === langB;
264
+ }
265
+ if (a.type === 'table_cell' || a.type === 'tableHeader' || a.type === 'table_header') {
266
+ var _a$attrs$colspan, _a$attrs4, _b$attrs$colspan, _b$attrs4, _a$attrs$rowspan, _a$attrs5, _b$attrs$rowspan, _b$attrs5;
267
+ var colspanA = (_a$attrs$colspan = (_a$attrs4 = a.attrs) === null || _a$attrs4 === void 0 ? void 0 : _a$attrs4.colspan) !== null && _a$attrs$colspan !== void 0 ? _a$attrs$colspan : 1;
268
+ var colspanB = (_b$attrs$colspan = (_b$attrs4 = b.attrs) === null || _b$attrs4 === void 0 ? void 0 : _b$attrs4.colspan) !== null && _b$attrs$colspan !== void 0 ? _b$attrs$colspan : 1;
269
+ var rowspanA = (_a$attrs$rowspan = (_a$attrs5 = a.attrs) === null || _a$attrs5 === void 0 ? void 0 : _a$attrs5.rowspan) !== null && _a$attrs$rowspan !== void 0 ? _a$attrs$rowspan : 1;
270
+ var rowspanB = (_b$attrs$rowspan = (_b$attrs5 = b.attrs) === null || _b$attrs5 === void 0 ? void 0 : _b$attrs5.rowspan) !== null && _b$attrs$rowspan !== void 0 ? _b$attrs$rowspan : 1;
271
+ return colspanA === colspanB && rowspanA === rowspanB;
272
+ }
273
+ return true;
274
+ }
472
275
  function lcsAlign(a, b, equals) {
473
276
  var n = a.length;
474
277
  var m = b.length;
@@ -477,9 +280,9 @@ function lcsAlign(a, b, equals) {
477
280
  }, function () {
478
281
  return Array(m + 1).fill(0);
479
282
  });
480
- for (var _i6 = n - 1; _i6 >= 0; _i6--) {
283
+ for (var _i4 = n - 1; _i4 >= 0; _i4--) {
481
284
  for (var _j = m - 1; _j >= 0; _j--) {
482
- dp[_i6][_j] = equals(a[_i6], b[_j]) ? 1 + dp[_i6 + 1][_j + 1] : Math.max(dp[_i6 + 1][_j], dp[_i6][_j + 1]);
285
+ dp[_i4][_j] = equals(a[_i4], b[_j]) ? 1 + dp[_i4 + 1][_j + 1] : Math.max(dp[_i4 + 1][_j], dp[_i4][_j + 1]);
483
286
  }
484
287
  }
485
288
  var pairs = [];
@@ -498,51 +301,212 @@ function lcsAlign(a, b, equals) {
498
301
  }
499
302
  return pairs;
500
303
  }
501
-
502
- /**
503
- * 对比两个HTML文档并生成结构化差异
504
- * @param {string} oldHtml - 旧HTML
505
- * @param {string} newHtml - 新HTML
506
- * @param {Array} extensions - Tiptap扩展数组
507
- * @returns {Object} 包含差异信息的对象
508
- */
304
+ function compareNodes(nodeA, nodeB) {
305
+ var path = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : [];
306
+ var diffs = [];
307
+ if ((nodeA === null || nodeA === void 0 ? void 0 : nodeA.type) !== (nodeB === null || nodeB === void 0 ? void 0 : nodeB.type)) {
308
+ if (nodeA) {
309
+ diffs.push({
310
+ type: 'delete',
311
+ path: _toConsumableArray(path),
312
+ node: nodeA
313
+ });
314
+ }
315
+ if (nodeB) {
316
+ diffs.push({
317
+ type: 'insert',
318
+ path: _toConsumableArray(path),
319
+ node: nodeB
320
+ });
321
+ }
322
+ return diffs;
323
+ }
324
+ if ((nodeA === null || nodeA === void 0 ? void 0 : nodeA.type) === 'text' && (nodeB === null || nodeB === void 0 ? void 0 : nodeB.type) === 'text') {
325
+ if (nodeA.text !== nodeB.text) {
326
+ var textDiffs = dmp.diff_main(nodeA.text || '', nodeB.text || '');
327
+ dmp.diff_cleanupSemantic(textDiffs);
328
+ var textOffset = 0;
329
+ textDiffs.forEach(function (_ref3) {
330
+ var _ref4 = _slicedToArray(_ref3, 2),
331
+ operation = _ref4[0],
332
+ text = _ref4[1];
333
+ if (operation === -1) {
334
+ var diffItem = {
335
+ type: 'delete',
336
+ path: _toConsumableArray(path),
337
+ textDiff: {
338
+ offset: textOffset,
339
+ length: text.length,
340
+ text: text,
341
+ operation: operation
342
+ }
343
+ };
344
+ diffs.push(diffItem);
345
+ } else if (operation === 1) {
346
+ var _diffItem = {
347
+ type: 'insert',
348
+ path: _toConsumableArray(path),
349
+ textDiff: {
350
+ offset: textOffset,
351
+ length: text.length,
352
+ text: text,
353
+ operation: operation
354
+ }
355
+ };
356
+ diffs.push(_diffItem);
357
+ textOffset += text.length;
358
+ } else {
359
+ textOffset += text.length;
360
+ }
361
+ });
362
+ }
363
+ if (!haveSameMarks(nodeA.marks, nodeB.marks)) {
364
+ diffs.push({
365
+ type: 'modify',
366
+ path: _toConsumableArray(path),
367
+ attrChange: {
368
+ key: 'marks',
369
+ oldValue: nodeA.marks || [],
370
+ newValue: nodeB.marks || []
371
+ }
372
+ });
373
+ }
374
+ return diffs;
375
+ }
376
+ if (nodeA && nodeB && isInlineContainer(nodeA) && isInlineContainer(nodeB)) {
377
+ diffs.push.apply(diffs, _toConsumableArray(compareInlineContainer(nodeA, nodeB, path)));
378
+ diffs.push.apply(diffs, _toConsumableArray(compareInlineContainerChildren(nodeA, nodeB, path)));
379
+ return diffs;
380
+ }
381
+ var attrsA = (nodeA === null || nodeA === void 0 ? void 0 : nodeA.attrs) || {};
382
+ var attrsB = (nodeB === null || nodeB === void 0 ? void 0 : nodeB.attrs) || {};
383
+ var allAttrKeys = new Set([].concat(_toConsumableArray(Object.keys(attrsA)), _toConsumableArray(Object.keys(attrsB))));
384
+ for (var _i5 = 0, _Array$from = Array.from(allAttrKeys); _i5 < _Array$from.length; _i5++) {
385
+ var key = _Array$from[_i5];
386
+ if (attrsA[key] !== attrsB[key]) {
387
+ diffs.push({
388
+ type: 'modify',
389
+ path: _toConsumableArray(path),
390
+ attrChange: {
391
+ key: key,
392
+ oldValue: attrsA[key],
393
+ newValue: attrsB[key]
394
+ }
395
+ });
396
+ }
397
+ }
398
+ var contentA = (nodeA === null || nodeA === void 0 ? void 0 : nodeA.content) || [];
399
+ var contentB = (nodeB === null || nodeB === void 0 ? void 0 : nodeB.content) || [];
400
+ var pairs = lcsAlign(contentA, contentB, nodesEqualForAlign);
401
+ var ai = 0;
402
+ var bi = 0;
403
+ var _iterator3 = _createForOfIteratorHelper(pairs),
404
+ _step3;
405
+ try {
406
+ for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
407
+ var _step3$value = _slicedToArray(_step3.value, 2),
408
+ i = _step3$value[0],
409
+ j = _step3$value[1];
410
+ while (ai < i) {
411
+ var _delNode = contentA[ai];
412
+ diffs.push({
413
+ type: 'delete',
414
+ path: [].concat(_toConsumableArray(path), [bi]),
415
+ node: _delNode
416
+ });
417
+ ai++;
418
+ }
419
+ while (bi < j) {
420
+ var _insNode = contentB[bi];
421
+ diffs.push({
422
+ type: 'insert',
423
+ path: [].concat(_toConsumableArray(path), [bi]),
424
+ node: _insNode
425
+ });
426
+ bi++;
427
+ }
428
+ var childA = contentA[i];
429
+ var childB = contentB[j];
430
+ diffs.push.apply(diffs, _toConsumableArray(compareNodes(childA, childB, [].concat(_toConsumableArray(path), [j]))));
431
+ ai = i + 1;
432
+ bi = j + 1;
433
+ }
434
+ } catch (err) {
435
+ _iterator3.e(err);
436
+ } finally {
437
+ _iterator3.f();
438
+ }
439
+ while (ai < contentA.length) {
440
+ var delNode = contentA[ai];
441
+ diffs.push({
442
+ type: 'delete',
443
+ path: [].concat(_toConsumableArray(path), [bi]),
444
+ node: delNode
445
+ });
446
+ ai++;
447
+ }
448
+ while (bi < contentB.length) {
449
+ var insNode = contentB[bi];
450
+ diffs.push({
451
+ type: 'insert',
452
+ path: [].concat(_toConsumableArray(path), [bi]),
453
+ node: insNode
454
+ });
455
+ bi++;
456
+ }
457
+ return diffs;
458
+ }
509
459
  export function compareDocuments(oldHtml, newHtml, extensions) {
510
- var docA = parseHtmlToDoc(oldHtml, extensions);
511
- var docB = parseHtmlToDoc(newHtml, extensions);
512
- var diffs = compareNodes(docA, docB);
513
- return {
514
- oldDoc: docA,
515
- newDoc: docB,
516
- diffs: diffs,
517
- hasChanges: diffs.length > 0
518
- };
460
+ try {
461
+ var docA = parseHtmlToDoc(oldHtml, extensions);
462
+ var docB = parseHtmlToDoc(newHtml, extensions);
463
+ var diffs = compareNodes(docA, docB);
464
+ return {
465
+ oldDoc: docA,
466
+ newDoc: docB,
467
+ diffs: diffs,
468
+ hasChanges: diffs.length > 0
469
+ };
470
+ } catch (error) {
471
+ console.error('对比文档时出错:', error);
472
+ return {
473
+ oldDoc: null,
474
+ newDoc: null,
475
+ diffs: [],
476
+ hasChanges: false
477
+ };
478
+ }
519
479
  }
520
-
521
- /**
522
- * 将路径转换为ProseMirror位置
523
- * @param {Array} path - 节点路径
524
- * @param {Object} doc - ProseMirror文档
525
- * @returns {number} 文档位置
526
- */
527
480
  export function pathToPos(path, doc) {
481
+ if (!path || path.length === 0) {
482
+ return 0;
483
+ }
484
+ if (!doc) {
485
+ throw new Error('文档节点不能为空');
486
+ }
528
487
  var pos = 0;
529
488
  var current = doc;
530
489
  for (var i = 0; i < path.length; i++) {
531
- var _current$childCount;
490
+ var _current$type, _current$childCount;
532
491
  var index = path[i];
533
- var contentStartOffset = current.type.name === 'doc' ? 0 : 1;
492
+ var contentStartOffset = ((_current$type = current.type) === null || _current$type === void 0 ? void 0 : _current$type.name) === 'doc' ? 0 : 1;
534
493
  var resolvedPos = pos + contentStartOffset;
535
494
  var childCount = (_current$childCount = current.childCount) !== null && _current$childCount !== void 0 ? _current$childCount : 0;
536
495
  var effectiveIndex = Math.min(index, childCount);
537
496
  for (var j = 0; j < effectiveIndex; j++) {
538
497
  var child = current.child(j);
498
+ if (!child) {
499
+ break;
500
+ }
539
501
  resolvedPos += child.nodeSize;
540
502
  }
541
503
  pos = resolvedPos;
542
504
  if (index < childCount) {
543
505
  current = current.child(index);
506
+ if (!current) {
507
+ break;
508
+ }
544
509
  } else {
545
- // Path points past the end in this doc (likely a deletion). Stop descending
546
510
  break;
547
511
  }
548
512
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ctzhian/tiptap",
3
- "version": "2.1.12",
3
+ "version": "2.1.14",
4
4
  "description": "基于 Tiptap 二次开发的编辑器组件",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.js",