y-mxgraph 0.8.5 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/binding/consistency.d.ts +56 -0
- package/binding/consistency.d.ts.map +1 -0
- package/binding/index.d.ts +38 -0
- package/binding/index.d.ts.map +1 -1
- package/binding/patch.d.ts +4 -2
- package/binding/patch.d.ts.map +1 -1
- package/iframe-bridge/provider.cjs +96 -26
- package/iframe-bridge/provider.cjs.map +1 -1
- package/iframe-bridge/provider.js +94 -24
- package/iframe-bridge/provider.js.map +1 -1
- package/iframe-bridge/server.cjs +38 -14
- package/iframe-bridge/server.cjs.map +1 -1
- package/iframe-bridge/server.js +30 -6
- package/iframe-bridge/server.js.map +1 -1
- package/{index-BhW4J2Zt.js → index-CN7tFgEG.js} +26 -4
- package/index-CN7tFgEG.js.map +1 -0
- package/{index-D3Hk2QcW.cjs → index-CqD1uLD3.cjs} +24 -2
- package/index-CqD1uLD3.cjs.map +1 -0
- package/index.d.ts +1 -1
- package/index.d.ts.map +1 -1
- package/models/mxGraphModel.d.ts.map +1 -1
- package/origin-C_FHB48F.cjs +6 -0
- package/origin-C_FHB48F.cjs.map +1 -0
- package/origin-Doo2uLkM.js +7 -0
- package/origin-Doo2uLkM.js.map +1 -0
- package/package.json +1 -1
- package/transform.cjs +1 -1
- package/transform.js +1 -1
- package/y-mxgraph.cjs +366 -143
- package/y-mxgraph.cjs.map +1 -1
- package/y-mxgraph.js +366 -143
- package/y-mxgraph.js.map +1 -1
- package/index-BhW4J2Zt.js.map +0 -1
- package/index-D3Hk2QcW.cjs.map +0 -1
package/y-mxgraph.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { g as getMap, b as backgroundKey, k as key, a as key$1, c as key$2, d as diagramOrderKey, m as mxCellOrderKey, e as key$3, p as parse, f as parse$1,
|
|
1
|
+
import { g as getMap, b as backgroundKey, k as key, a as key$1, c as key$2, d as diagramOrderKey, m as mxCellOrderKey, e as key$3, p as parse, f as parse$1, s as serializer, h as serialize, y as ydoc2xml, x as xml2ydoc } from "./index-CN7tFgEG.js";
|
|
2
2
|
import * as Y from "yjs";
|
|
3
3
|
import { colord } from "colord";
|
|
4
4
|
function patchValueToXmlAttr(value) {
|
|
@@ -74,9 +74,15 @@ const DIFF_UPDATE = "u";
|
|
|
74
74
|
const docSnapshots = /* @__PURE__ */ new WeakMap();
|
|
75
75
|
function insertAfterUnique(orderArr, id, previous, fallbackToEnd = false) {
|
|
76
76
|
const currentIds = orderArr.toArray();
|
|
77
|
-
let anchorPos = previous ? currentIds.indexOf(previous) : -1;
|
|
77
|
+
let anchorPos = previous != null ? currentIds.indexOf(previous) : -1;
|
|
78
78
|
if (anchorPos === -1 && fallbackToEnd) anchorPos = currentIds.length - 1;
|
|
79
79
|
let targetIndex = anchorPos + 1;
|
|
80
|
+
if (previous === "" && id !== "0" && id !== "1") {
|
|
81
|
+
const layerIndex = currentIds.indexOf("1");
|
|
82
|
+
if (layerIndex >= 0) {
|
|
83
|
+
targetIndex = layerIndex + 1;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
80
86
|
const existingIndex = currentIds.indexOf(id);
|
|
81
87
|
if (existingIndex === -1) {
|
|
82
88
|
orderArr.insert(targetIndex, [id]);
|
|
@@ -131,34 +137,41 @@ function pruneEmptyPatch(patch) {
|
|
|
131
137
|
}
|
|
132
138
|
function applyFilePatch(doc, patch, options) {
|
|
133
139
|
doc.transact(() => {
|
|
140
|
+
var _a, _b;
|
|
134
141
|
const mxfile = doc.getMap(key$1);
|
|
135
142
|
if (patch[DIFF_REMOVE]) {
|
|
136
143
|
const diagramsMap = mxfile.get(key$2);
|
|
137
|
-
const orderArr = mxfile.get(
|
|
138
|
-
|
|
139
|
-
);
|
|
140
|
-
ensureUniqueOrder(orderArr);
|
|
141
|
-
const orderList = orderArr.toArray();
|
|
144
|
+
const orderArr = mxfile.get(diagramOrderKey);
|
|
145
|
+
if (orderArr) ensureUniqueOrder(orderArr);
|
|
142
146
|
const removeIds = patch[DIFF_REMOVE];
|
|
143
147
|
if (removeIds && removeIds.length) {
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
148
|
+
if (orderArr) {
|
|
149
|
+
const orderList = orderArr.toArray();
|
|
150
|
+
const indexList = removeIds.map((id) => orderList.indexOf(id)).filter((i) => i !== -1).sort((a, b) => b - a);
|
|
151
|
+
indexList.forEach((idx) => orderArr.delete(idx, 1));
|
|
152
|
+
}
|
|
153
|
+
if (diagramsMap) {
|
|
154
|
+
removeIds.forEach((id) => {
|
|
155
|
+
if (diagramsMap.has(id)) {
|
|
156
|
+
diagramsMap.delete(id);
|
|
157
|
+
}
|
|
158
|
+
});
|
|
159
|
+
}
|
|
147
160
|
}
|
|
148
161
|
}
|
|
149
162
|
if (patch[DIFF_INSERT]) {
|
|
150
163
|
const diagramsMap = mxfile.get(key$2);
|
|
151
|
-
const orderArr = mxfile.get(
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
164
|
+
const orderArr = mxfile.get(diagramOrderKey);
|
|
165
|
+
if (orderArr) {
|
|
166
|
+
ensureUniqueOrder(orderArr);
|
|
167
|
+
const currentOrder = orderArr.toArray();
|
|
168
|
+
if (currentOrder.length === 0 && diagramsMap && diagramsMap.size > 0) {
|
|
169
|
+
const allIds = Array.from(diagramsMap.keys());
|
|
170
|
+
orderArr.push(allIds);
|
|
171
|
+
}
|
|
172
|
+
ensureUniqueOrder(orderArr);
|
|
159
173
|
}
|
|
160
|
-
|
|
161
|
-
const existingIds = orderArr.toArray();
|
|
174
|
+
const existingIds = (_a = orderArr == null ? void 0 : orderArr.toArray()) != null ? _a : [];
|
|
162
175
|
const existingIndex = /* @__PURE__ */ new Map();
|
|
163
176
|
existingIds.forEach((id, idx) => existingIndex.set(id, idx));
|
|
164
177
|
const inserts = patch[DIFF_INSERT].map((item, order) => {
|
|
@@ -169,7 +182,7 @@ function applyFilePatch(doc, patch, options) {
|
|
|
169
182
|
);
|
|
170
183
|
return {
|
|
171
184
|
id: item.id,
|
|
172
|
-
previous: item.previous
|
|
185
|
+
previous: item.previous === void 0 ? null : item.previous,
|
|
173
186
|
diagramElement,
|
|
174
187
|
order
|
|
175
188
|
};
|
|
@@ -211,16 +224,19 @@ function applyFilePatch(doc, patch, options) {
|
|
|
211
224
|
return b.order - a.order;
|
|
212
225
|
});
|
|
213
226
|
for (const item of enriched) {
|
|
214
|
-
diagramsMap
|
|
215
|
-
|
|
227
|
+
if (diagramsMap) {
|
|
228
|
+
diagramsMap.set(item.id, item.diagramElement);
|
|
229
|
+
}
|
|
230
|
+
if (orderArr) {
|
|
231
|
+
const anchorArg = item.anchorId === "" ? "" : (_b = item.anchorId) != null ? _b : null;
|
|
232
|
+
insertAfterUnique(orderArr, item.id, anchorArg);
|
|
233
|
+
}
|
|
216
234
|
}
|
|
217
235
|
}
|
|
218
236
|
if (patch[DIFF_UPDATE]) {
|
|
219
237
|
Object.keys(patch[DIFF_UPDATE]).forEach((id) => {
|
|
220
|
-
const diagramsMap = mxfile.get(
|
|
221
|
-
|
|
222
|
-
);
|
|
223
|
-
const diagram = diagramsMap.get(id);
|
|
238
|
+
const diagramsMap = mxfile.get(key$2);
|
|
239
|
+
const diagram = diagramsMap == null ? void 0 : diagramsMap.get(id);
|
|
224
240
|
if (diagram) {
|
|
225
241
|
const update = patch[DIFF_UPDATE][id];
|
|
226
242
|
if ("name" in update) {
|
|
@@ -234,132 +250,151 @@ function applyFilePatch(doc, patch, options) {
|
|
|
234
250
|
}
|
|
235
251
|
if (update.cells) {
|
|
236
252
|
const yMxGraphModel = diagram.get(key);
|
|
237
|
-
if (!yMxGraphModel)
|
|
253
|
+
if (!yMxGraphModel) {
|
|
254
|
+
console.warn(
|
|
255
|
+
"[y-mxgraph] applyFilePatch: yMxGraphModel not found for diagram, skipping cells update"
|
|
256
|
+
);
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
238
259
|
const cellsMap = yMxGraphModel.get(key$3);
|
|
239
260
|
const orderArr = yMxGraphModel.get(mxCellOrderKey);
|
|
240
|
-
if (!cellsMap
|
|
241
|
-
|
|
261
|
+
if (!cellsMap && !orderArr) {
|
|
262
|
+
console.warn(
|
|
263
|
+
"[y-mxgraph] applyFilePatch: both cellsMap and orderArr missing, skipping cells update"
|
|
264
|
+
);
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
267
|
+
if (orderArr) ensureUniqueOrder(orderArr);
|
|
242
268
|
if (update.cells[DIFF_REMOVE] && update.cells[DIFF_REMOVE].length) {
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
269
|
+
if (orderArr) {
|
|
270
|
+
const orderIds = orderArr.toArray();
|
|
271
|
+
const removeIndexList = update.cells[DIFF_REMOVE].map(
|
|
272
|
+
(cid) => orderIds.indexOf(cid)
|
|
273
|
+
).filter((i) => i !== -1).sort((a, b) => b - a);
|
|
274
|
+
removeIndexList.forEach((idx) => orderArr.delete(idx, 1));
|
|
275
|
+
}
|
|
276
|
+
if (cellsMap) {
|
|
277
|
+
update.cells[DIFF_REMOVE].forEach((cid) => {
|
|
278
|
+
if (cellsMap.has(cid)) {
|
|
279
|
+
cellsMap.delete(cid);
|
|
280
|
+
}
|
|
281
|
+
});
|
|
282
|
+
}
|
|
249
283
|
}
|
|
250
284
|
if (update.cells[DIFF_INSERT] && update.cells[DIFF_INSERT].length) {
|
|
251
285
|
for (const item of update.cells[DIFF_INSERT]) {
|
|
252
286
|
const id2 = item["id"];
|
|
253
287
|
if (!id2) continue;
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
anchorId = null;
|
|
288
|
+
if (cellsMap) {
|
|
289
|
+
const xmlElement = new Y.XmlElement("mxCell");
|
|
290
|
+
Object.keys(item).forEach((key2) => {
|
|
291
|
+
if (key2 === "previous") return;
|
|
292
|
+
xmlElement.setAttribute(key2, item[key2]);
|
|
293
|
+
});
|
|
294
|
+
cellsMap.set(id2, xmlElement);
|
|
295
|
+
}
|
|
296
|
+
if (orderArr) {
|
|
297
|
+
const previous = item["previous"];
|
|
298
|
+
const parent = item["parent"];
|
|
299
|
+
let anchorId = null;
|
|
300
|
+
let fallbackToEnd = true;
|
|
301
|
+
if (typeof previous !== "undefined") {
|
|
302
|
+
if (previous === "") {
|
|
303
|
+
anchorId = "";
|
|
271
304
|
fallbackToEnd = false;
|
|
305
|
+
} else {
|
|
306
|
+
anchorId = previous;
|
|
307
|
+
fallbackToEnd = true;
|
|
272
308
|
}
|
|
273
|
-
} else {
|
|
274
|
-
anchorId =
|
|
309
|
+
} else if (parent) {
|
|
310
|
+
anchorId = parent;
|
|
275
311
|
fallbackToEnd = true;
|
|
276
312
|
}
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
313
|
+
insertAfterUnique(
|
|
314
|
+
orderArr,
|
|
315
|
+
id2,
|
|
316
|
+
anchorId,
|
|
317
|
+
fallbackToEnd
|
|
318
|
+
);
|
|
280
319
|
}
|
|
281
|
-
insertAfterUnique(
|
|
282
|
-
orderArr,
|
|
283
|
-
id2,
|
|
284
|
-
anchorId,
|
|
285
|
-
fallbackToEnd
|
|
286
|
-
);
|
|
287
320
|
}
|
|
288
321
|
}
|
|
289
322
|
if (update.cells[DIFF_UPDATE]) {
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
323
|
+
if (cellsMap) {
|
|
324
|
+
Object.keys(update.cells[DIFF_UPDATE]).forEach((cid) => {
|
|
325
|
+
const updateObj = update.cells[DIFF_UPDATE][cid];
|
|
326
|
+
const cell = cellsMap.get(cid);
|
|
327
|
+
if (cell) {
|
|
328
|
+
Object.keys(updateObj).forEach((k) => {
|
|
329
|
+
if (k === "previous") return;
|
|
330
|
+
cell.setAttribute(k, updateObj[k]);
|
|
331
|
+
});
|
|
332
|
+
} else {
|
|
333
|
+
console.warn(
|
|
334
|
+
`[y-mxgraph] applyFilePatch: cell ${cid} not found in cellsMap, skipping update`
|
|
335
|
+
);
|
|
336
|
+
}
|
|
337
|
+
});
|
|
338
|
+
}
|
|
339
|
+
if (cellsMap && orderArr) {
|
|
340
|
+
const reorderEntries = Object.keys(update.cells[DIFF_UPDATE]).map((cellId) => {
|
|
341
|
+
const updateObj = update.cells[DIFF_UPDATE][cellId];
|
|
342
|
+
const hasPrev = "previous" in updateObj;
|
|
343
|
+
const hasParent = "parent" in updateObj;
|
|
344
|
+
if (!hasPrev && !hasParent) return null;
|
|
345
|
+
const prevVal = hasPrev ? updateObj.previous : void 0;
|
|
346
|
+
const parentVal = hasParent ? updateObj.parent : void 0;
|
|
347
|
+
let anchorId = null;
|
|
348
|
+
let fallbackToEnd = true;
|
|
349
|
+
if (hasPrev) {
|
|
350
|
+
if (prevVal === "") {
|
|
351
|
+
anchorId = "";
|
|
352
|
+
fallbackToEnd = false;
|
|
353
|
+
} else if (prevVal === null || typeof prevVal === "undefined") {
|
|
354
|
+
anchorId = null;
|
|
313
355
|
fallbackToEnd = true;
|
|
314
356
|
} else {
|
|
315
|
-
anchorId =
|
|
316
|
-
fallbackToEnd =
|
|
357
|
+
anchorId = prevVal;
|
|
358
|
+
fallbackToEnd = true;
|
|
317
359
|
}
|
|
318
|
-
} else {
|
|
319
|
-
anchorId =
|
|
360
|
+
} else if (parentVal) {
|
|
361
|
+
anchorId = parentVal;
|
|
320
362
|
fallbackToEnd = true;
|
|
321
363
|
}
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
364
|
+
return { cellId, anchorId, fallbackToEnd, updateObj };
|
|
365
|
+
}).filter((e) => e !== null);
|
|
366
|
+
const applyReorder = (entry) => {
|
|
367
|
+
const { cellId, anchorId, fallbackToEnd, updateObj } = entry;
|
|
368
|
+
const currentIds = orderArr.toArray();
|
|
369
|
+
const currentIndex = currentIds.indexOf(cellId);
|
|
370
|
+
if (currentIndex === -1) {
|
|
371
|
+
let newCell = cellsMap.get(cellId);
|
|
372
|
+
if (!newCell) {
|
|
373
|
+
newCell = new Y.XmlElement("mxCell");
|
|
374
|
+
newCell.setAttribute("id", cellId);
|
|
375
|
+
Object.keys(updateObj).forEach((k) => {
|
|
376
|
+
if (k === "previous") return;
|
|
377
|
+
newCell.setAttribute(k, updateObj[k]);
|
|
378
|
+
});
|
|
379
|
+
cellsMap.set(cellId, newCell);
|
|
380
|
+
}
|
|
381
|
+
insertAfterUnique(orderArr, cellId, anchorId, fallbackToEnd);
|
|
382
|
+
return;
|
|
338
383
|
}
|
|
339
|
-
insertAfterUnique(
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
);
|
|
345
|
-
return;
|
|
346
|
-
}
|
|
347
|
-
insertAfterUnique(
|
|
348
|
-
orderArr,
|
|
349
|
-
cellId,
|
|
350
|
-
anchorId,
|
|
351
|
-
fallbackToEnd
|
|
352
|
-
);
|
|
353
|
-
});
|
|
384
|
+
insertAfterUnique(orderArr, cellId, anchorId, fallbackToEnd);
|
|
385
|
+
};
|
|
386
|
+
reorderEntries.filter((e) => e.anchorId === "" || e.anchorId === null).forEach(applyReorder);
|
|
387
|
+
reorderEntries.filter((e) => e.anchorId !== "" && e.anchorId !== null).forEach(applyReorder);
|
|
388
|
+
}
|
|
354
389
|
}
|
|
355
390
|
}
|
|
356
391
|
if ("previous" in update) {
|
|
357
|
-
const previous = update.previous
|
|
358
|
-
const orderArr = mxfile.get(
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
392
|
+
const previous = Object.prototype.hasOwnProperty.call(update, "previous") ? update.previous : null;
|
|
393
|
+
const orderArr = mxfile.get(diagramOrderKey);
|
|
394
|
+
if (orderArr) {
|
|
395
|
+
ensureUniqueOrder(orderArr);
|
|
396
|
+
insertAfterUnique(orderArr, id, previous, false);
|
|
397
|
+
}
|
|
363
398
|
}
|
|
364
399
|
}
|
|
365
400
|
});
|
|
@@ -380,7 +415,7 @@ function initDocSnapshot(doc, resetSnapshot = false) {
|
|
|
380
415
|
cellAttrs: /* @__PURE__ */ new Map(),
|
|
381
416
|
diagramBackground: /* @__PURE__ */ new Map()
|
|
382
417
|
};
|
|
383
|
-
const diagrams = diagramOrder.map((id) => diagramsMap.get(id)).filter((d) => !!d);
|
|
418
|
+
const diagrams = diagramOrder.map((id) => diagramsMap == null ? void 0 : diagramsMap.get(id)).filter((d) => !!d);
|
|
384
419
|
for (const d of diagrams) {
|
|
385
420
|
const did = d.get("id") || "";
|
|
386
421
|
if (!did) continue;
|
|
@@ -415,7 +450,7 @@ function initDocSnapshot(doc, resetSnapshot = false) {
|
|
|
415
450
|
}
|
|
416
451
|
}
|
|
417
452
|
function generatePatch(events, explicitDoc) {
|
|
418
|
-
var _a, _b, _c, _d, _e;
|
|
453
|
+
var _a, _b, _c, _d, _e, _f;
|
|
419
454
|
const patch = {};
|
|
420
455
|
const doc = (_b = (_a = events[0]) == null ? void 0 : _a.transaction) == null ? void 0 : _b.doc;
|
|
421
456
|
if (!doc) return patch;
|
|
@@ -449,7 +484,7 @@ function generatePatch(events, explicitDoc) {
|
|
|
449
484
|
};
|
|
450
485
|
const orderIds = (_c = orderArr == null ? void 0 : orderArr.toArray()) != null ? _c : [];
|
|
451
486
|
const currDiagramOrder = orderIds.length > 0 ? orderIds : diagramsMap ? Array.from(diagramsMap.keys()) : [];
|
|
452
|
-
const diagramsList = currDiagramOrder.map((id) => diagramsMap.get(id)).filter((d) => !!d);
|
|
487
|
+
const diagramsList = currDiagramOrder.map((id) => diagramsMap == null ? void 0 : diagramsMap.get(id)).filter((d) => !!d);
|
|
453
488
|
const currCellsOrder = /* @__PURE__ */ new Map();
|
|
454
489
|
const cellAttrMap = /* @__PURE__ */ new Map();
|
|
455
490
|
const currDiagramBackground = /* @__PURE__ */ new Map();
|
|
@@ -495,7 +530,7 @@ function generatePatch(events, explicitDoc) {
|
|
|
495
530
|
for (const id of inserted) {
|
|
496
531
|
const index = currDiagramOrder.indexOf(id);
|
|
497
532
|
const previous = index <= 0 ? "" : currDiagramOrder[index - 1];
|
|
498
|
-
const yDiagram = diagramsMap.get(id);
|
|
533
|
+
const yDiagram = diagramsMap == null ? void 0 : diagramsMap.get(id);
|
|
499
534
|
if (!yDiagram) continue;
|
|
500
535
|
const data = serializer({ diagram: serialize(yDiagram) });
|
|
501
536
|
patch[DIFF_INSERT].push({ id, previous, data });
|
|
@@ -504,7 +539,8 @@ function generatePatch(events, explicitDoc) {
|
|
|
504
539
|
}
|
|
505
540
|
const prevNeighbor = (order, id) => {
|
|
506
541
|
const i = order.indexOf(id);
|
|
507
|
-
|
|
542
|
+
if (i === -1) return null;
|
|
543
|
+
return i === 0 ? "" : order[i - 1];
|
|
508
544
|
};
|
|
509
545
|
const common = currDiagramOrder.filter((id) => prevSet.has(id) && id);
|
|
510
546
|
for (const id of common) {
|
|
@@ -553,7 +589,8 @@ function generatePatch(events, explicitDoc) {
|
|
|
553
589
|
}
|
|
554
590
|
const prevNeighbor = (order, id) => {
|
|
555
591
|
const i = order.indexOf(id);
|
|
556
|
-
|
|
592
|
+
if (i === -1) return null;
|
|
593
|
+
return i === 0 ? "" : order[i - 1];
|
|
557
594
|
};
|
|
558
595
|
const commonCells = currCells.filter((cid) => prevSet.has(cid) && cid);
|
|
559
596
|
for (const cid of commonCells) {
|
|
@@ -630,7 +667,7 @@ function generatePatch(events, explicitDoc) {
|
|
|
630
667
|
cellsPatch[DIFF_UPDATE] = cellsPatch[DIFF_UPDATE] || {};
|
|
631
668
|
const cellUpdate = cellsPatch[DIFF_UPDATE][cellId] = cellsPatch[DIFF_UPDATE][cellId] || {};
|
|
632
669
|
for (const key2 of Array.from(changed)) {
|
|
633
|
-
cellUpdate[key2] = el.getAttribute(key2)
|
|
670
|
+
cellUpdate[key2] = (_d = el.getAttribute(key2)) != null ? _d : "";
|
|
634
671
|
}
|
|
635
672
|
}
|
|
636
673
|
if (prevDiagramOrder) {
|
|
@@ -659,8 +696,8 @@ function generatePatch(events, explicitDoc) {
|
|
|
659
696
|
const cellUpdate = updateBucket[cid] = updateBucket[cid] || {};
|
|
660
697
|
let changed = false;
|
|
661
698
|
for (const k of keys) {
|
|
662
|
-
const pv = (
|
|
663
|
-
const cv = (
|
|
699
|
+
const pv = (_e = prevAttrs[k]) != null ? _e : "";
|
|
700
|
+
const cv = (_f = currAttrs[k]) != null ? _f : "";
|
|
664
701
|
if (pv !== cv) {
|
|
665
702
|
cellUpdate[k] = cv;
|
|
666
703
|
changed = true;
|
|
@@ -1346,6 +1383,91 @@ function bindCollaborator(file, options) {
|
|
|
1346
1383
|
cleanupAwareness == null ? void 0 : cleanupAwareness();
|
|
1347
1384
|
};
|
|
1348
1385
|
}
|
|
1386
|
+
function checkConsistency(doc, fileData) {
|
|
1387
|
+
try {
|
|
1388
|
+
const xmlFromYdoc = ydoc2xml(doc);
|
|
1389
|
+
if (xmlFromYdoc === fileData) return true;
|
|
1390
|
+
const diagramCountFromYdoc = (xmlFromYdoc.match(/<diagram /g) || []).length;
|
|
1391
|
+
const diagramCountFromFile = (fileData.match(/<diagram /g) || []).length;
|
|
1392
|
+
if (diagramCountFromYdoc !== diagramCountFromFile) return false;
|
|
1393
|
+
const cellCountFromYdoc = (xmlFromYdoc.match(/<mxCell /g) || []).length;
|
|
1394
|
+
const cellCountFromFile = (fileData.match(/<mxCell /g) || []).length;
|
|
1395
|
+
if (cellCountFromYdoc !== cellCountFromFile) return false;
|
|
1396
|
+
return true;
|
|
1397
|
+
} catch (e) {
|
|
1398
|
+
return false;
|
|
1399
|
+
}
|
|
1400
|
+
}
|
|
1401
|
+
class ConsistencyChecker {
|
|
1402
|
+
constructor(doc, getFileData, options = {}) {
|
|
1403
|
+
this.doc = doc;
|
|
1404
|
+
this.getFileData = getFileData;
|
|
1405
|
+
this.intervalId = null;
|
|
1406
|
+
this.handlers = /* @__PURE__ */ new Set();
|
|
1407
|
+
this.consecutiveDriftCount = 0;
|
|
1408
|
+
var _a, _b;
|
|
1409
|
+
this.source = (_a = options.source) != null ? _a : "binding";
|
|
1410
|
+
this.maxAutoFixAttempts = (_b = options.maxAutoFixAttempts) != null ? _b : 3;
|
|
1411
|
+
}
|
|
1412
|
+
/** 注册 drift 事件监听 */
|
|
1413
|
+
onDrift(handler) {
|
|
1414
|
+
this.handlers.add(handler);
|
|
1415
|
+
return () => this.handlers.delete(handler);
|
|
1416
|
+
}
|
|
1417
|
+
/** 启动定期检查(毫秒间隔),0 或负数表示禁用 */
|
|
1418
|
+
start(intervalMs) {
|
|
1419
|
+
this.stop();
|
|
1420
|
+
if (intervalMs <= 0) return;
|
|
1421
|
+
this.intervalId = setInterval(() => {
|
|
1422
|
+
this.check();
|
|
1423
|
+
}, intervalMs);
|
|
1424
|
+
}
|
|
1425
|
+
/** 停止定期检查 */
|
|
1426
|
+
stop() {
|
|
1427
|
+
if (this.intervalId != null) {
|
|
1428
|
+
clearInterval(this.intervalId);
|
|
1429
|
+
this.intervalId = null;
|
|
1430
|
+
}
|
|
1431
|
+
}
|
|
1432
|
+
/**
|
|
1433
|
+
* 执行一次一致性检查。
|
|
1434
|
+
* @returns true = 一致,false = 存在 drift
|
|
1435
|
+
*/
|
|
1436
|
+
check() {
|
|
1437
|
+
const consistent = checkConsistency(this.doc, this.getFileData());
|
|
1438
|
+
if (consistent) {
|
|
1439
|
+
this.consecutiveDriftCount = 0;
|
|
1440
|
+
return true;
|
|
1441
|
+
}
|
|
1442
|
+
this.consecutiveDriftCount++;
|
|
1443
|
+
const event = {
|
|
1444
|
+
timestamp: Date.now(),
|
|
1445
|
+
source: this.source,
|
|
1446
|
+
direction: "unknown",
|
|
1447
|
+
details: `drift detected (consecutive #${this.consecutiveDriftCount})`
|
|
1448
|
+
};
|
|
1449
|
+
this.handlers.forEach((handler) => {
|
|
1450
|
+
try {
|
|
1451
|
+
handler(event);
|
|
1452
|
+
} catch (e) {
|
|
1453
|
+
console.warn("[y-mxgraph] drift handler error:", e);
|
|
1454
|
+
}
|
|
1455
|
+
});
|
|
1456
|
+
return false;
|
|
1457
|
+
}
|
|
1458
|
+
/** 重置连续 drift 计数(forceSync 成功后调用) */
|
|
1459
|
+
resetDriftCount() {
|
|
1460
|
+
this.consecutiveDriftCount = 0;
|
|
1461
|
+
}
|
|
1462
|
+
/** 是否已超过最大自动修复次数 */
|
|
1463
|
+
get shouldStopAutoFix() {
|
|
1464
|
+
return this.consecutiveDriftCount >= this.maxAutoFixAttempts;
|
|
1465
|
+
}
|
|
1466
|
+
destroy() {
|
|
1467
|
+
this.stop();
|
|
1468
|
+
this.handlers.clear();
|
|
1469
|
+
}
|
|
1470
|
+
}
|
|
1349
1471
|
const defaultApplyFileData = (file, xml) => {
|
|
1350
1472
|
file.ui.setFileData(xml);
|
|
1351
1473
|
};
|
|
@@ -1457,6 +1579,7 @@ class Binding {
|
|
|
1457
1579
|
this.suppressLocalApply = false;
|
|
1458
1580
|
this.docInitialized = false;
|
|
1459
1581
|
this.ui = null;
|
|
1582
|
+
this.debouncedForceSync = null;
|
|
1460
1583
|
const {
|
|
1461
1584
|
doc,
|
|
1462
1585
|
awareness,
|
|
@@ -1466,12 +1589,15 @@ class Binding {
|
|
|
1466
1589
|
initialContent = "replace",
|
|
1467
1590
|
applyFileData = defaultApplyFileData,
|
|
1468
1591
|
disableBeforeUnload = true,
|
|
1469
|
-
transformPatch
|
|
1592
|
+
transformPatch,
|
|
1593
|
+
syncOnOrderMismatch = false
|
|
1470
1594
|
} = options;
|
|
1471
1595
|
this.doc = doc;
|
|
1472
1596
|
this.file = file;
|
|
1473
1597
|
this.initialContentStrategy = initialContent;
|
|
1474
1598
|
this.transformPatch = transformPatch;
|
|
1599
|
+
this.applyFileData = applyFileData;
|
|
1600
|
+
this.syncOnOrderMismatch = syncOnOrderMismatch;
|
|
1475
1601
|
const ui = file.getUi();
|
|
1476
1602
|
const graph = ui.editor.graph;
|
|
1477
1603
|
this.mxGraphModel = graph.model;
|
|
@@ -1522,7 +1648,6 @@ class Binding {
|
|
|
1522
1648
|
this.mxGraphModel.addListener("change", this.mxListener);
|
|
1523
1649
|
this.docObserver = (events, transaction) => {
|
|
1524
1650
|
if (transaction.local && transaction.origin === LOCAL_ORIGIN) {
|
|
1525
|
-
generatePatch(events);
|
|
1526
1651
|
return;
|
|
1527
1652
|
}
|
|
1528
1653
|
if (this.shouldReplaceWhenDocHasData && !transaction.local) {
|
|
@@ -1549,7 +1674,8 @@ class Binding {
|
|
|
1549
1674
|
this.docInitialized = true;
|
|
1550
1675
|
}
|
|
1551
1676
|
const patch = generatePatch(events);
|
|
1552
|
-
|
|
1677
|
+
const patchKeys = Object.keys(patch);
|
|
1678
|
+
if (patchKeys.length === 0) return;
|
|
1553
1679
|
this.suppressLocalApply = true;
|
|
1554
1680
|
try {
|
|
1555
1681
|
file.patch([patch]);
|
|
@@ -1558,6 +1684,14 @@ class Binding {
|
|
|
1558
1684
|
} finally {
|
|
1559
1685
|
this.suppressLocalApply = false;
|
|
1560
1686
|
}
|
|
1687
|
+
if (this.syncOnOrderMismatch && patch.u && this.checkOrderMismatch(patch)) {
|
|
1688
|
+
console.warn("[y-mxgraph] order mismatch detected, debounced forceSync (file → ydoc)");
|
|
1689
|
+
if (this.debouncedForceSync) clearTimeout(this.debouncedForceSync);
|
|
1690
|
+
this.debouncedForceSync = setTimeout(() => {
|
|
1691
|
+
this.debouncedForceSync = null;
|
|
1692
|
+
this.forceSync("file-to-ydoc");
|
|
1693
|
+
}, 300);
|
|
1694
|
+
}
|
|
1561
1695
|
};
|
|
1562
1696
|
doc.getMap(key$1).observeDeep(this.docObserver);
|
|
1563
1697
|
if (awareness) {
|
|
@@ -1571,6 +1705,29 @@ class Binding {
|
|
|
1571
1705
|
if (undoManager) {
|
|
1572
1706
|
this.cleanupUndoManager = bindUndoManager(doc, file, undoManager);
|
|
1573
1707
|
}
|
|
1708
|
+
const {
|
|
1709
|
+
consistencyCheckInterval,
|
|
1710
|
+
onDrift
|
|
1711
|
+
} = options;
|
|
1712
|
+
if (consistencyCheckInterval && consistencyCheckInterval > 0) {
|
|
1713
|
+
this.consistencyChecker = new ConsistencyChecker(doc, () => file.data, {
|
|
1714
|
+
source: "binding"
|
|
1715
|
+
});
|
|
1716
|
+
if (onDrift) {
|
|
1717
|
+
this.consistencyChecker.onDrift(onDrift);
|
|
1718
|
+
}
|
|
1719
|
+
this.consistencyChecker.onDrift((event) => {
|
|
1720
|
+
var _a;
|
|
1721
|
+
if ((_a = this.consistencyChecker) == null ? void 0 : _a.shouldStopAutoFix) {
|
|
1722
|
+
console.warn(
|
|
1723
|
+
`[y-mxgraph] 连续 ${event.details} 次 drift,停止自动 forceSync`
|
|
1724
|
+
);
|
|
1725
|
+
return;
|
|
1726
|
+
}
|
|
1727
|
+
this.forceSync("ydoc-to-file");
|
|
1728
|
+
});
|
|
1729
|
+
this.consistencyChecker.start(consistencyCheckInterval);
|
|
1730
|
+
}
|
|
1574
1731
|
}
|
|
1575
1732
|
/** replace 策略下,构造时 doc 为空,现在 doc 有数据时需要强制替换 */
|
|
1576
1733
|
get shouldReplaceWhenDocHasData() {
|
|
@@ -1587,17 +1744,83 @@ class Binding {
|
|
|
1587
1744
|
this.ui.editor.setStatus("");
|
|
1588
1745
|
(_a = this.ui.currentFile) == null ? void 0 : _a.setModified(false);
|
|
1589
1746
|
}
|
|
1747
|
+
/**
|
|
1748
|
+
* 强制同步 ydoc 与 file,修复检测到的不一致。
|
|
1749
|
+
*
|
|
1750
|
+
* @param direction - 同步方向
|
|
1751
|
+
* - `ydoc-to-file`(默认):用 ydoc 数据覆盖 file
|
|
1752
|
+
* - `file-to-ydoc`:用 file 数据覆盖 ydoc
|
|
1753
|
+
*/
|
|
1754
|
+
forceSync(direction = "ydoc-to-file") {
|
|
1755
|
+
var _a;
|
|
1756
|
+
if (direction === "ydoc-to-file") {
|
|
1757
|
+
const xml = ydoc2xml(this.doc);
|
|
1758
|
+
if (!xml || !xml.includes("<diagram")) return;
|
|
1759
|
+
this.suppressLocalApply = true;
|
|
1760
|
+
try {
|
|
1761
|
+
this.applyFileData(this.file, xml);
|
|
1762
|
+
this.file.setShadowPages(this.file.ui.clonePages(this.file.ui.pages));
|
|
1763
|
+
initDocSnapshot(this.doc, false);
|
|
1764
|
+
this.resetEditorStatus();
|
|
1765
|
+
} finally {
|
|
1766
|
+
this.suppressLocalApply = false;
|
|
1767
|
+
}
|
|
1768
|
+
} else {
|
|
1769
|
+
this.doc.transact(() => {
|
|
1770
|
+
xml2ydoc(this.file.data, this.doc);
|
|
1771
|
+
initDocSnapshot(this.doc, false);
|
|
1772
|
+
}, LOCAL_ORIGIN);
|
|
1773
|
+
}
|
|
1774
|
+
(_a = this.consistencyChecker) == null ? void 0 : _a.resetDriftCount();
|
|
1775
|
+
}
|
|
1776
|
+
checkOrderMismatch(patch) {
|
|
1777
|
+
var _a;
|
|
1778
|
+
if (!patch.u) return false;
|
|
1779
|
+
for (const [did, update] of Object.entries(patch.u)) {
|
|
1780
|
+
if (!((_a = update.cells) == null ? void 0 : _a.u)) continue;
|
|
1781
|
+
for (const [cid, cellUpdate] of Object.entries(update.cells.u)) {
|
|
1782
|
+
if (!("previous" in cellUpdate)) continue;
|
|
1783
|
+
const mxfile = this.doc.getMap(key$1);
|
|
1784
|
+
const diagramsMap = mxfile.get(key$2);
|
|
1785
|
+
const diagram = diagramsMap == null ? void 0 : diagramsMap.get(did);
|
|
1786
|
+
if (!diagram) continue;
|
|
1787
|
+
const gm = diagram.get(key);
|
|
1788
|
+
if (!gm) continue;
|
|
1789
|
+
const orderArr = gm.get(mxCellOrderKey);
|
|
1790
|
+
if (!orderArr) continue;
|
|
1791
|
+
const ids = orderArr.toArray();
|
|
1792
|
+
const idx = ids.indexOf(cid);
|
|
1793
|
+
if (idx === -1) continue;
|
|
1794
|
+
const expectedPrev = idx === 0 ? "" : ids[idx - 1];
|
|
1795
|
+
const actualPrev = cellUpdate.previous;
|
|
1796
|
+
if (expectedPrev !== actualPrev) {
|
|
1797
|
+
console.log(`[y-mxgraph] order mismatch: cell=${cid}, ydoc prev=${JSON.stringify(expectedPrev)}, patch prev=${JSON.stringify(actualPrev)}`);
|
|
1798
|
+
return true;
|
|
1799
|
+
}
|
|
1800
|
+
}
|
|
1801
|
+
}
|
|
1802
|
+
return false;
|
|
1803
|
+
}
|
|
1804
|
+
/**
|
|
1805
|
+
* 手动触发一次一致性检查。
|
|
1806
|
+
* @returns true = 一致,false = 存在 drift
|
|
1807
|
+
*/
|
|
1808
|
+
checkConsistency() {
|
|
1809
|
+
if (!this.consistencyChecker) return true;
|
|
1810
|
+
return this.consistencyChecker.check();
|
|
1811
|
+
}
|
|
1590
1812
|
/**
|
|
1591
1813
|
* 销毁绑定,解除所有监听器
|
|
1592
1814
|
* @param deep - 是否深度清理(包括 awareness/undoManager),默认 false
|
|
1593
1815
|
*/
|
|
1594
1816
|
destroy(deep = false) {
|
|
1595
|
-
var _a, _b;
|
|
1817
|
+
var _a, _b, _c;
|
|
1596
1818
|
this.mxGraphModel.removeListener("change", this.mxListener);
|
|
1597
1819
|
this.doc.getMap(key$1).unobserveDeep(this.docObserver);
|
|
1820
|
+
(_a = this.consistencyChecker) == null ? void 0 : _a.destroy();
|
|
1598
1821
|
if (deep) {
|
|
1599
|
-
(
|
|
1600
|
-
(
|
|
1822
|
+
(_b = this.cleanupCollaborator) == null ? void 0 : _b.call(this);
|
|
1823
|
+
(_c = this.cleanupUndoManager) == null ? void 0 : _c.call(this);
|
|
1601
1824
|
}
|
|
1602
1825
|
}
|
|
1603
1826
|
/**
|