y-mxgraph 0.3.1 → 0.4.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/README.zh-CN.md CHANGED
@@ -82,7 +82,7 @@ App.main((app) => {
82
82
 
83
83
  ```ts
84
84
  // Server(父页面)
85
- import { createIframeBridgeServer } from '@y-mxgraph/iframe-bridge/server';
85
+ import { createIframeBridgeServer } from 'y-mxgraph/iframe-bridge/server';
86
86
 
87
87
  const doc = new Y.Doc();
88
88
  const provider = new WebrtcProvider(roomName, doc, { signaling });
@@ -90,7 +90,7 @@ const bridge = createIframeBridgeServer(doc, provider.awareness);
90
90
  bridge.addIframe(iframeElement, 'child-1');
91
91
 
92
92
  // Provider(iframe 子页面)
93
- import { createIframeBridgeProvider } from '@y-mxgraph/iframe-bridge/provider';
93
+ import { createIframeBridgeProvider } from 'y-mxgraph/iframe-bridge/provider';
94
94
 
95
95
  const doc = new Y.Doc();
96
96
  const awareness = new Awareness(doc);
@@ -11,7 +11,13 @@ export type InitialContentStrategy = "replace" | "merge-remote" | "merge-client"
11
11
  export interface BindDrawioFileOptions {
12
12
  doc: Y.Doc;
13
13
  awareness?: Awareness;
14
- undoManager?: Y.UndoManager;
14
+ /**
15
+ * UndoManager 实例,用于支持 undo/redo。
16
+ * - 传入 Y.UndoManager 实例:绑定到 draw.io 的 undoManager
17
+ * - 传入 false:跳过绑定(用于 iframe-bridge 等外部接管场景)
18
+ * - 不传或 undefined:不绑定 undoManager
19
+ */
20
+ undoManager?: Y.UndoManager | false;
15
21
  mouseMoveThrottle?: number;
16
22
  cursor?: boolean | {
17
23
  userNameKey?: string;
@@ -55,6 +61,8 @@ export interface BindDrawioFileOptions {
55
61
  export declare class Binding {
56
62
  /** Y.Doc 实例,用于协同数据存储 */
57
63
  readonly doc: Y.Doc;
64
+ /** draw.io file 实例 */
65
+ readonly file: DrawioFile;
58
66
  /** mxGraph 的数据模型,用于监听本地变更 */
59
67
  private mxGraphModel;
60
68
  /** 本地变更抑制标志,防止循环同步 */
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/binding/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,KAAK,CAAC;AACzB,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAiBvD,OAAO,KAAK,EAAE,UAAU,EAA0B,MAAM,iBAAiB,CAAC;AAE1E;;;;;GAKG;AACH,MAAM,MAAM,sBAAsB,GAC9B,SAAS,GACT,cAAc,GACd,cAAc,CAAC;AAEnB,MAAM,WAAW,qBAAqB;IACpC,GAAG,EAAE,CAAC,CAAC,GAAG,CAAC;IACX,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,WAAW,CAAC,EAAE,CAAC,CAAC,WAAW,CAAC;IAC5B,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,MAAM,CAAC,EACH,OAAO,GACP;QACE,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,YAAY,CAAC,EAAE,MAAM,CAAC;KACvB,CAAC;IACN;;OAEG;IACH,cAAc,CAAC,EAAE,sBAAsB,CAAC;IACxC;;;;;;;;OAQG;IACH,aAAa,CAAC,EAAE,CAAC,IAAI,EAAE,UAAU,EAAE,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IACxD;;;;;;;;;;OAUG;IACH,mBAAmB,CAAC,EAAE,OAAO,CAAC;CAC/B;AA0KD;;;;;;;GAOG;AACH,qBAAa,OAAO;IAClB,wBAAwB;IACxB,QAAQ,CAAC,GAAG,EAAE,CAAC,CAAC,GAAG,CAAC;IACpB,6BAA6B;IAC7B,OAAO,CAAC,YAAY,CAAe;IACnC,sBAAsB;IACtB,OAAO,CAAC,kBAAkB,CAAS;IACnC,4BAA4B;IAC5B,OAAO,CAAC,cAAc,CAAS;IAC/B,2BAA2B;IAC3B,OAAO,CAAC,UAAU,CAAa;IAC/B,oBAAoB;IACpB,OAAO,CAAC,WAAW,CAKT;IACV,gCAAgC;IAChC,OAAO,CAAC,mBAAmB,CAAC,CAAa;IACzC,yBAAyB;IACzB,OAAO,CAAC,kBAAkB,CAAC,CAAa;IACxC,aAAa;IACb,OAAO,CAAC,sBAAsB,CAAyB;IACvD,0CAA0C;IAC1C,OAAO,CAAC,EAAE,CAAyB;IAEnC,+CAA+C;IAC/C,OAAO,KAAK,2BAA2B,GAEtC;gBAEW,IAAI,EAAE,UAAU,EAAE,OAAO,EAAE,qBAAqB;IAoJ5D;;;OAGG;IACH,OAAO,CAAC,iBAAiB;IAOzB;;;OAGG;IACH,OAAO,CAAC,IAAI,UAAQ,GAAG,IAAI;IAS3B;;;;;;;;;;;;;;OAcG;IACH,MAAM,CAAC,oBAAoB,CAAC,SAAS,SAAc,GAAG,MAAM;IAa5D;;;OAGG;IACH,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,UAAU,EAAE,OAAO,EAAE,qBAAqB,GAAG,OAAO;CAGzE"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/binding/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,KAAK,CAAC;AACzB,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAiBvD,OAAO,KAAK,EAAE,UAAU,EAA0B,MAAM,iBAAiB,CAAC;AAE1E;;;;;GAKG;AACH,MAAM,MAAM,sBAAsB,GAC9B,SAAS,GACT,cAAc,GACd,cAAc,CAAC;AAEnB,MAAM,WAAW,qBAAqB;IACpC,GAAG,EAAE,CAAC,CAAC,GAAG,CAAC;IACX,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB;;;;;OAKG;IACH,WAAW,CAAC,EAAE,CAAC,CAAC,WAAW,GAAG,KAAK,CAAC;IACpC,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,MAAM,CAAC,EACH,OAAO,GACP;QACE,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,YAAY,CAAC,EAAE,MAAM,CAAC;KACvB,CAAC;IACN;;OAEG;IACH,cAAc,CAAC,EAAE,sBAAsB,CAAC;IACxC;;;;;;;;OAQG;IACH,aAAa,CAAC,EAAE,CAAC,IAAI,EAAE,UAAU,EAAE,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IACxD;;;;;;;;;;OAUG;IACH,mBAAmB,CAAC,EAAE,OAAO,CAAC;CAC/B;AA0KD;;;;;;;GAOG;AACH,qBAAa,OAAO;IAClB,wBAAwB;IACxB,QAAQ,CAAC,GAAG,EAAE,CAAC,CAAC,GAAG,CAAC;IACpB,sBAAsB;IACtB,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAC1B,6BAA6B;IAC7B,OAAO,CAAC,YAAY,CAAe;IACnC,sBAAsB;IACtB,OAAO,CAAC,kBAAkB,CAAS;IACnC,4BAA4B;IAC5B,OAAO,CAAC,cAAc,CAAS;IAC/B,2BAA2B;IAC3B,OAAO,CAAC,UAAU,CAAa;IAC/B,oBAAoB;IACpB,OAAO,CAAC,WAAW,CAKT;IACV,gCAAgC;IAChC,OAAO,CAAC,mBAAmB,CAAC,CAAa;IACzC,yBAAyB;IACzB,OAAO,CAAC,kBAAkB,CAAC,CAAa;IACxC,aAAa;IACb,OAAO,CAAC,sBAAsB,CAAyB;IACvD,0CAA0C;IAC1C,OAAO,CAAC,EAAE,CAAyB;IAEnC,+CAA+C;IAC/C,OAAO,KAAK,2BAA2B,GAEtC;gBAEW,IAAI,EAAE,UAAU,EAAE,OAAO,EAAE,qBAAqB;IAqJ5D;;;OAGG;IACH,OAAO,CAAC,iBAAiB;IAOzB;;;OAGG;IACH,OAAO,CAAC,IAAI,UAAQ,GAAG,IAAI;IAS3B;;;;;;;;;;;;;;OAcG;IACH,MAAM,CAAC,oBAAoB,CAAC,SAAS,SAAc,GAAG,MAAM;IAa5D;;;OAGG;IACH,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,UAAU,EAAE,OAAO,EAAE,qBAAqB,GAAG,OAAO;CAGzE"}
@@ -0,0 +1,2 @@
1
+ export { IFRAME_ORIGIN } from "@y-mxgraph/iframe-bridge";
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/iframe-bridge/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC"}
@@ -0,0 +1,246 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const Y = require("yjs");
4
+ const awareness = require("y-protocols/awareness");
5
+ function _interopNamespaceDefault(e) {
6
+ const n = Object.create(null, { [Symbol.toStringTag]: { value: "Module" } });
7
+ if (e) {
8
+ for (const k in e) {
9
+ if (k !== "default") {
10
+ const d = Object.getOwnPropertyDescriptor(e, k);
11
+ Object.defineProperty(n, k, d.get ? d : {
12
+ enumerable: true,
13
+ get: () => e[k]
14
+ });
15
+ }
16
+ }
17
+ }
18
+ n.default = e;
19
+ return Object.freeze(n);
20
+ }
21
+ const Y__namespace = /* @__PURE__ */ _interopNamespaceDefault(Y);
22
+ function createMxEventObject(name, props) {
23
+ const _props = props || {};
24
+ return {
25
+ name,
26
+ getName: () => name,
27
+ getProperty: (k) => _props[k]
28
+ };
29
+ }
30
+ function readVarUint(data, pos) {
31
+ let result = 0;
32
+ let shift = 0;
33
+ let byte;
34
+ do {
35
+ byte = data[pos++];
36
+ result |= (byte & 127) << shift;
37
+ shift += 7;
38
+ } while (byte >= 128);
39
+ return [result >>> 0, pos];
40
+ }
41
+ function writeVarUint(value) {
42
+ const bytes = [];
43
+ while (value > 127) {
44
+ bytes.push(value & 127 | 128);
45
+ value >>>= 7;
46
+ }
47
+ bytes.push(value);
48
+ return bytes;
49
+ }
50
+ function readVarString(data, pos) {
51
+ const [len, pos2] = readVarUint(data, pos);
52
+ const str = new TextDecoder().decode(data.subarray(pos2, pos2 + len));
53
+ return [str, pos2 + len];
54
+ }
55
+ function writeVarString(str) {
56
+ const encoded = new TextEncoder().encode(str);
57
+ return [...writeVarUint(encoded.length), ...encoded];
58
+ }
59
+ function remapClientIdInUpdate(update, fromId, toId) {
60
+ const result = [];
61
+ let pos = 0;
62
+ const [count, pos2] = readVarUint(update, pos);
63
+ pos = pos2;
64
+ result.push(...writeVarUint(count));
65
+ for (let i = 0; i < count; i++) {
66
+ const [clientID, pos3] = readVarUint(update, pos);
67
+ pos = pos3;
68
+ const [clock, pos4] = readVarUint(update, pos);
69
+ pos = pos4;
70
+ const [state, pos5] = readVarString(update, pos);
71
+ pos = pos5;
72
+ const mappedId = clientID === fromId ? toId : clientID;
73
+ result.push(...writeVarUint(mappedId));
74
+ result.push(...writeVarUint(clock));
75
+ result.push(...writeVarString(state));
76
+ }
77
+ return new Uint8Array(result);
78
+ }
79
+ function createIframeBridgeProvider(ydoc, awareness$1) {
80
+ let applyingParentUpdate = false;
81
+ let serverClientId = null;
82
+ let currentCleanup = null;
83
+ let currentMxLike = null;
84
+ const onYdocUpdate = (update) => {
85
+ if (applyingParentUpdate) return;
86
+ if (currentMxLike) {
87
+ if (currentMxLike.indexOfNextAdd < currentMxLike.history.length) {
88
+ currentMxLike.history.splice(
89
+ currentMxLike.indexOfNextAdd,
90
+ currentMxLike.history.length - currentMxLike.indexOfNextAdd
91
+ );
92
+ }
93
+ currentMxLike.history.push({});
94
+ currentMxLike.indexOfNextAdd = currentMxLike.history.length;
95
+ currentMxLike.fireEvent(createMxEventObject("add", { edit: { changes: [] } }));
96
+ }
97
+ window.parent.postMessage(
98
+ { type: "ydoc-update", payload: Array.from(update) },
99
+ "*"
100
+ );
101
+ };
102
+ const onAwarenessUpdate = ({
103
+ added,
104
+ updated,
105
+ removed
106
+ }) => {
107
+ if (applyingParentUpdate) return;
108
+ const changes = [...added, ...updated, ...removed];
109
+ if (changes.length === 0) return;
110
+ const update = awareness.encodeAwarenessUpdate(awareness$1, changes);
111
+ const remapped = serverClientId != null ? remapClientIdInUpdate(update, awareness$1.clientID, serverClientId) : update;
112
+ window.parent.postMessage(
113
+ { type: "awareness-update", payload: Array.from(remapped) },
114
+ "*"
115
+ );
116
+ };
117
+ const onMessage = (event) => {
118
+ if (event.source !== window.parent) return;
119
+ const { type, payload, serverClientId: receivedServerId } = event.data;
120
+ if (type === "pong" && receivedServerId != null) {
121
+ serverClientId = receivedServerId;
122
+ return;
123
+ }
124
+ if (type === "ydoc-sync" || type === "ydoc-update") {
125
+ applyingParentUpdate = true;
126
+ Y__namespace.applyUpdate(ydoc, new Uint8Array(payload));
127
+ applyingParentUpdate = false;
128
+ } else if (type === "awareness-sync" || type === "awareness-update") {
129
+ if (receivedServerId != null) {
130
+ serverClientId = receivedServerId;
131
+ }
132
+ const raw = new Uint8Array(payload);
133
+ const remapped = serverClientId != null ? remapClientIdInUpdate(raw, serverClientId, awareness$1.clientID) : raw;
134
+ applyingParentUpdate = true;
135
+ awareness.applyAwarenessUpdate(awareness$1, remapped, null);
136
+ applyingParentUpdate = false;
137
+ } else if (type === "undo" && currentMxLike) {
138
+ applyingParentUpdate = true;
139
+ if (currentMxLike.indexOfNextAdd > 0) currentMxLike.indexOfNextAdd--;
140
+ currentMxLike.fireEvent(createMxEventObject("undo", { edit: { changes: [] } }));
141
+ applyingParentUpdate = false;
142
+ } else if (type === "redo" && currentMxLike) {
143
+ applyingParentUpdate = true;
144
+ if (currentMxLike.indexOfNextAdd < currentMxLike.history.length) currentMxLike.indexOfNextAdd++;
145
+ currentMxLike.fireEvent(createMxEventObject("redo", { edit: { changes: [] } }));
146
+ applyingParentUpdate = false;
147
+ } else if (type === "clear" && currentMxLike) {
148
+ applyingParentUpdate = true;
149
+ currentMxLike.history = [];
150
+ currentMxLike.indexOfNextAdd = 0;
151
+ currentMxLike.fireEvent(createMxEventObject("clear"));
152
+ applyingParentUpdate = false;
153
+ }
154
+ };
155
+ ydoc.on("update", onYdocUpdate);
156
+ awareness$1.on("update", onAwarenessUpdate);
157
+ window.addEventListener("message", onMessage);
158
+ window.parent.postMessage({ type: "init" }, "*");
159
+ return {
160
+ get serverClientId() {
161
+ return serverClientId;
162
+ },
163
+ takeoverUndoManager(file) {
164
+ if (currentCleanup) {
165
+ currentCleanup();
166
+ }
167
+ const editor = file.getUi().editor;
168
+ const originUndoManager = editor.undoManager;
169
+ const pairs = [];
170
+ const raw = Array.isArray(originUndoManager == null ? void 0 : originUndoManager.eventListeners) ? originUndoManager.eventListeners : [];
171
+ for (let i = 0; i + 1 < raw.length; i += 2) {
172
+ const key = String(raw[i]);
173
+ const fn = raw[i + 1];
174
+ pairs.push([key, fn]);
175
+ }
176
+ const mxLike = {
177
+ eventListeners: [],
178
+ history: [],
179
+ indexOfNextAdd: 0,
180
+ addListener(name, fn) {
181
+ this.eventListeners.push(name, fn);
182
+ },
183
+ fireEvent(evt) {
184
+ var _a;
185
+ const eventName = (evt == null ? void 0 : evt.name) || (((_a = evt == null ? void 0 : evt.getName) == null ? void 0 : _a.call(evt)) ?? "");
186
+ for (let i = 0; i + 1 < this.eventListeners.length; i += 2) {
187
+ const key = this.eventListeners[i];
188
+ const listener = this.eventListeners[i + 1];
189
+ if (key === eventName) {
190
+ try {
191
+ listener(this, evt);
192
+ } catch (e) {
193
+ console.warn("[iframe-bridge] undoManager event listener error:", e);
194
+ }
195
+ }
196
+ }
197
+ },
198
+ canUndo() {
199
+ return this.indexOfNextAdd > 0;
200
+ },
201
+ canRedo() {
202
+ return this.indexOfNextAdd < this.history.length;
203
+ },
204
+ undo() {
205
+ if (!applyingParentUpdate) {
206
+ window.parent.postMessage({ type: "undo" }, "*");
207
+ }
208
+ },
209
+ redo() {
210
+ if (!applyingParentUpdate) {
211
+ window.parent.postMessage({ type: "redo" }, "*");
212
+ }
213
+ },
214
+ undoableEditHappened() {
215
+ }
216
+ };
217
+ pairs.forEach(([key, fn]) => {
218
+ const k = key.toLowerCase();
219
+ if (k === "add" || k === "clear" || k === "undo" || k === "redo") {
220
+ mxLike.addListener(k, fn);
221
+ }
222
+ });
223
+ currentMxLike = mxLike;
224
+ editor.undoManager = mxLike;
225
+ editor.undoListener = function() {
226
+ };
227
+ const cleanup = () => {
228
+ editor.undoManager = originUndoManager;
229
+ editor.undoListener = originUndoManager == null ? void 0 : originUndoManager.undoListener;
230
+ currentMxLike = null;
231
+ };
232
+ currentCleanup = cleanup;
233
+ return cleanup;
234
+ },
235
+ destroy: () => {
236
+ ydoc.off("update", onYdocUpdate);
237
+ awareness$1.off("update", onAwarenessUpdate);
238
+ window.removeEventListener("message", onMessage);
239
+ if (currentCleanup) {
240
+ currentCleanup();
241
+ }
242
+ }
243
+ };
244
+ }
245
+ exports.createIframeBridgeProvider = createIframeBridgeProvider;
246
+ //# sourceMappingURL=provider.cjs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provider.cjs.js","sources":["../../../iframe-bridge/src/provider.ts"],"sourcesContent":["import * as Y from \"yjs\";\nimport {\n Awareness,\n applyAwarenessUpdate,\n encodeAwarenessUpdate,\n} from \"y-protocols/awareness\";\n\ntype ListenerFn = (sender: unknown, evt?: unknown) => void;\n\nfunction createMxEventObject(name: string, props?: Record<string, unknown>) {\n const _props = props || {};\n return {\n name,\n getName: () => name,\n getProperty: (k: string) => _props[k],\n };\n}\n\ntype MxLike = Record<string, unknown> & {\n eventListeners: Array<string | ListenerFn>;\n history: unknown[];\n indexOfNextAdd: number;\n addListener(name: string, fn: ListenerFn): void;\n fireEvent(evt: unknown): void;\n canUndo(): boolean;\n canRedo(): boolean;\n undo(): void;\n redo(): void;\n undoableEditHappened(_edit: unknown): void;\n};\n\nexport interface DrawioEditor {\n undoManager?: {\n eventListeners?: unknown[];\n [key: string]: unknown;\n };\n undoListener?: (...args: unknown[]) => void;\n}\n\nexport interface DrawioFile {\n getUi(): { editor: DrawioEditor };\n}\n\nexport interface IframeBridgeProvider {\n serverClientId: number | null;\n takeoverUndoManager: (file: DrawioFile) => () => void;\n destroy: () => void;\n}\n\nfunction readVarUint(data: Uint8Array, pos: number): [number, number] {\n let result = 0;\n let shift = 0;\n let byte: number;\n do {\n byte = data[pos++];\n result |= (byte & 0x7f) << shift;\n shift += 7;\n } while (byte >= 0x80);\n return [result >>> 0, pos];\n}\n\nfunction writeVarUint(value: number): number[] {\n const bytes: number[] = [];\n while (value > 0x7f) {\n bytes.push((value & 0x7f) | 0x80);\n value >>>= 7;\n }\n bytes.push(value);\n return bytes;\n}\n\nfunction readVarString(data: Uint8Array, pos: number): [string, number] {\n const [len, pos2] = readVarUint(data, pos);\n const str = new TextDecoder().decode(data.subarray(pos2, pos2 + len));\n return [str, pos2 + len];\n}\n\nfunction writeVarString(str: string): number[] {\n const encoded = new TextEncoder().encode(str);\n return [...writeVarUint(encoded.length), ...encoded];\n}\n\nfunction remapClientIdInUpdate(\n update: Uint8Array,\n fromId: number,\n toId: number,\n): Uint8Array {\n const result: number[] = [];\n let pos = 0;\n\n const [count, pos2] = readVarUint(update, pos);\n pos = pos2;\n result.push(...writeVarUint(count));\n\n for (let i = 0; i < count; i++) {\n const [clientID, pos3] = readVarUint(update, pos);\n pos = pos3;\n const [clock, pos4] = readVarUint(update, pos);\n pos = pos4;\n const [state, pos5] = readVarString(update, pos);\n pos = pos5;\n\n const mappedId = clientID === fromId ? toId : clientID;\n result.push(...writeVarUint(mappedId));\n result.push(...writeVarUint(clock));\n result.push(...writeVarString(state));\n }\n\n return new Uint8Array(result);\n}\n\nexport function createIframeBridgeProvider(\n ydoc: Y.Doc,\n awareness: Awareness,\n): IframeBridgeProvider {\n let applyingParentUpdate = false;\n let serverClientId: number | null = null;\n let currentCleanup: (() => void) | null = null;\n let currentMxLike: MxLike | null = null;\n\n const onYdocUpdate = (update: Uint8Array) => {\n if (applyingParentUpdate) return;\n // 本地编辑,添加 history 条目\n if (currentMxLike) {\n if (currentMxLike.indexOfNextAdd < currentMxLike.history.length) {\n currentMxLike.history.splice(\n currentMxLike.indexOfNextAdd,\n currentMxLike.history.length - currentMxLike.indexOfNextAdd,\n );\n }\n currentMxLike.history.push({});\n currentMxLike.indexOfNextAdd = currentMxLike.history.length;\n currentMxLike.fireEvent(createMxEventObject(\"add\", { edit: { changes: [] } }));\n }\n window.parent.postMessage(\n { type: \"ydoc-update\", payload: Array.from(update) },\n \"*\",\n );\n };\n\n const onAwarenessUpdate = ({\n added,\n updated,\n removed,\n }: {\n added: number[];\n updated: number[];\n removed: number[];\n }) => {\n if (applyingParentUpdate) return;\n const changes = [...added, ...updated, ...removed];\n if (changes.length === 0) return;\n\n const update = encodeAwarenessUpdate(awareness, changes);\n const remapped =\n serverClientId != null\n ? remapClientIdInUpdate(update, awareness.clientID, serverClientId)\n : update;\n\n window.parent.postMessage(\n { type: \"awareness-update\", payload: Array.from(remapped) },\n \"*\",\n );\n };\n\n const onMessage = (event: MessageEvent) => {\n if (event.source !== window.parent) return;\n const { type, payload, serverClientId: receivedServerId } = event.data;\n\n if (type === \"pong\" && receivedServerId != null) {\n serverClientId = receivedServerId;\n return;\n }\n\n if (type === \"ydoc-sync\" || type === \"ydoc-update\") {\n applyingParentUpdate = true;\n Y.applyUpdate(ydoc, new Uint8Array(payload));\n applyingParentUpdate = false;\n } else if (type === \"awareness-sync\" || type === \"awareness-update\") {\n if (receivedServerId != null) {\n serverClientId = receivedServerId;\n }\n\n const raw = new Uint8Array(payload);\n const remapped =\n serverClientId != null\n ? remapClientIdInUpdate(raw, serverClientId, awareness.clientID)\n : raw;\n\n applyingParentUpdate = true;\n applyAwarenessUpdate(awareness, remapped, null);\n applyingParentUpdate = false;\n } else if (type === \"undo\" && currentMxLike) {\n applyingParentUpdate = true;\n if (currentMxLike.indexOfNextAdd > 0) currentMxLike.indexOfNextAdd--;\n currentMxLike.fireEvent(createMxEventObject(\"undo\", { edit: { changes: [] } }));\n applyingParentUpdate = false;\n } else if (type === \"redo\" && currentMxLike) {\n applyingParentUpdate = true;\n if (currentMxLike.indexOfNextAdd < currentMxLike.history.length) currentMxLike.indexOfNextAdd++;\n currentMxLike.fireEvent(createMxEventObject(\"redo\", { edit: { changes: [] } }));\n applyingParentUpdate = false;\n } else if (type === \"clear\" && currentMxLike) {\n applyingParentUpdate = true;\n currentMxLike.history = [];\n currentMxLike.indexOfNextAdd = 0;\n currentMxLike.fireEvent(createMxEventObject(\"clear\"));\n applyingParentUpdate = false;\n }\n };\n\n ydoc.on(\"update\", onYdocUpdate);\n awareness.on(\"update\", onAwarenessUpdate);\n window.addEventListener(\"message\", onMessage);\n\n window.parent.postMessage({ type: \"init\" }, \"*\");\n\n return {\n get serverClientId() {\n return serverClientId;\n },\n takeoverUndoManager(file: DrawioFile) {\n if (currentCleanup) {\n currentCleanup();\n }\n\n const editor = file.getUi().editor;\n const originUndoManager = editor.undoManager;\n\n const pairs: Array<[string, ListenerFn]> = [];\n const raw = Array.isArray(originUndoManager?.eventListeners)\n ? (originUndoManager.eventListeners as unknown[])\n : [];\n for (let i = 0; i + 1 < raw.length; i += 2) {\n const key = String(raw[i]);\n const fn = raw[i + 1] as ListenerFn;\n pairs.push([key, fn]);\n }\n\n const mxLike: MxLike = {\n eventListeners: [] as Array<string | ListenerFn>,\n history: [] as unknown[],\n indexOfNextAdd: 0,\n\n addListener(name: string, fn: ListenerFn) {\n this.eventListeners.push(name, fn);\n },\n\n fireEvent(evt: unknown) {\n const eventName: string =\n (evt as { name?: string } | undefined)?.name ||\n ((evt as { getName?: () => string } | undefined)?.getName?.() ?? \"\");\n for (let i = 0; i + 1 < this.eventListeners.length; i += 2) {\n const key = this.eventListeners[i];\n const listener = this.eventListeners[i + 1] as ListenerFn;\n if (key === eventName) {\n try {\n listener(this, evt);\n } catch (e) {\n console.warn(\"[iframe-bridge] undoManager event listener error:\", e);\n }\n }\n }\n },\n\n canUndo(): boolean {\n return this.indexOfNextAdd > 0;\n },\n\n canRedo(): boolean {\n return this.indexOfNextAdd < this.history.length;\n },\n\n undo() {\n if (!applyingParentUpdate) {\n window.parent.postMessage({ type: \"undo\" }, \"*\");\n }\n },\n\n redo() {\n if (!applyingParentUpdate) {\n window.parent.postMessage({ type: \"redo\" }, \"*\");\n }\n },\n\n undoableEditHappened() {\n // no-op\n },\n };\n\n pairs.forEach(([key, fn]) => {\n const k = key.toLowerCase();\n if (k === \"add\" || k === \"clear\" || k === \"undo\" || k === \"redo\") {\n mxLike.addListener(k, fn);\n }\n });\n\n currentMxLike = mxLike;\n editor.undoManager = mxLike as any;\n editor.undoListener = function () {};\n\n const cleanup = () => {\n editor.undoManager = originUndoManager;\n editor.undoListener = originUndoManager?.undoListener as ((...args: unknown[]) => void) | undefined;\n currentMxLike = null;\n };\n\n currentCleanup = cleanup;\n return cleanup;\n },\n destroy: () => {\n ydoc.off(\"update\", onYdocUpdate);\n awareness.off(\"update\", onAwarenessUpdate);\n window.removeEventListener(\"message\", onMessage);\n if (currentCleanup) {\n currentCleanup();\n }\n },\n };\n}\n"],"names":["awareness","encodeAwarenessUpdate","Y","applyAwarenessUpdate"],"mappings":"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"}
@@ -0,0 +1,229 @@
1
+ import * as Y from "yjs";
2
+ import { encodeAwarenessUpdate, applyAwarenessUpdate } from "y-protocols/awareness";
3
+ function createMxEventObject(name, props) {
4
+ const _props = props || {};
5
+ return {
6
+ name,
7
+ getName: () => name,
8
+ getProperty: (k) => _props[k]
9
+ };
10
+ }
11
+ function readVarUint(data, pos) {
12
+ let result = 0;
13
+ let shift = 0;
14
+ let byte;
15
+ do {
16
+ byte = data[pos++];
17
+ result |= (byte & 127) << shift;
18
+ shift += 7;
19
+ } while (byte >= 128);
20
+ return [result >>> 0, pos];
21
+ }
22
+ function writeVarUint(value) {
23
+ const bytes = [];
24
+ while (value > 127) {
25
+ bytes.push(value & 127 | 128);
26
+ value >>>= 7;
27
+ }
28
+ bytes.push(value);
29
+ return bytes;
30
+ }
31
+ function readVarString(data, pos) {
32
+ const [len, pos2] = readVarUint(data, pos);
33
+ const str = new TextDecoder().decode(data.subarray(pos2, pos2 + len));
34
+ return [str, pos2 + len];
35
+ }
36
+ function writeVarString(str) {
37
+ const encoded = new TextEncoder().encode(str);
38
+ return [...writeVarUint(encoded.length), ...encoded];
39
+ }
40
+ function remapClientIdInUpdate(update, fromId, toId) {
41
+ const result = [];
42
+ let pos = 0;
43
+ const [count, pos2] = readVarUint(update, pos);
44
+ pos = pos2;
45
+ result.push(...writeVarUint(count));
46
+ for (let i = 0; i < count; i++) {
47
+ const [clientID, pos3] = readVarUint(update, pos);
48
+ pos = pos3;
49
+ const [clock, pos4] = readVarUint(update, pos);
50
+ pos = pos4;
51
+ const [state, pos5] = readVarString(update, pos);
52
+ pos = pos5;
53
+ const mappedId = clientID === fromId ? toId : clientID;
54
+ result.push(...writeVarUint(mappedId));
55
+ result.push(...writeVarUint(clock));
56
+ result.push(...writeVarString(state));
57
+ }
58
+ return new Uint8Array(result);
59
+ }
60
+ function createIframeBridgeProvider(ydoc, awareness) {
61
+ let applyingParentUpdate = false;
62
+ let serverClientId = null;
63
+ let currentCleanup = null;
64
+ let currentMxLike = null;
65
+ const onYdocUpdate = (update) => {
66
+ if (applyingParentUpdate) return;
67
+ if (currentMxLike) {
68
+ if (currentMxLike.indexOfNextAdd < currentMxLike.history.length) {
69
+ currentMxLike.history.splice(
70
+ currentMxLike.indexOfNextAdd,
71
+ currentMxLike.history.length - currentMxLike.indexOfNextAdd
72
+ );
73
+ }
74
+ currentMxLike.history.push({});
75
+ currentMxLike.indexOfNextAdd = currentMxLike.history.length;
76
+ currentMxLike.fireEvent(createMxEventObject("add", { edit: { changes: [] } }));
77
+ }
78
+ window.parent.postMessage(
79
+ { type: "ydoc-update", payload: Array.from(update) },
80
+ "*"
81
+ );
82
+ };
83
+ const onAwarenessUpdate = ({
84
+ added,
85
+ updated,
86
+ removed
87
+ }) => {
88
+ if (applyingParentUpdate) return;
89
+ const changes = [...added, ...updated, ...removed];
90
+ if (changes.length === 0) return;
91
+ const update = encodeAwarenessUpdate(awareness, changes);
92
+ const remapped = serverClientId != null ? remapClientIdInUpdate(update, awareness.clientID, serverClientId) : update;
93
+ window.parent.postMessage(
94
+ { type: "awareness-update", payload: Array.from(remapped) },
95
+ "*"
96
+ );
97
+ };
98
+ const onMessage = (event) => {
99
+ if (event.source !== window.parent) return;
100
+ const { type, payload, serverClientId: receivedServerId } = event.data;
101
+ if (type === "pong" && receivedServerId != null) {
102
+ serverClientId = receivedServerId;
103
+ return;
104
+ }
105
+ if (type === "ydoc-sync" || type === "ydoc-update") {
106
+ applyingParentUpdate = true;
107
+ Y.applyUpdate(ydoc, new Uint8Array(payload));
108
+ applyingParentUpdate = false;
109
+ } else if (type === "awareness-sync" || type === "awareness-update") {
110
+ if (receivedServerId != null) {
111
+ serverClientId = receivedServerId;
112
+ }
113
+ const raw = new Uint8Array(payload);
114
+ const remapped = serverClientId != null ? remapClientIdInUpdate(raw, serverClientId, awareness.clientID) : raw;
115
+ applyingParentUpdate = true;
116
+ applyAwarenessUpdate(awareness, remapped, null);
117
+ applyingParentUpdate = false;
118
+ } else if (type === "undo" && currentMxLike) {
119
+ applyingParentUpdate = true;
120
+ if (currentMxLike.indexOfNextAdd > 0) currentMxLike.indexOfNextAdd--;
121
+ currentMxLike.fireEvent(createMxEventObject("undo", { edit: { changes: [] } }));
122
+ applyingParentUpdate = false;
123
+ } else if (type === "redo" && currentMxLike) {
124
+ applyingParentUpdate = true;
125
+ if (currentMxLike.indexOfNextAdd < currentMxLike.history.length) currentMxLike.indexOfNextAdd++;
126
+ currentMxLike.fireEvent(createMxEventObject("redo", { edit: { changes: [] } }));
127
+ applyingParentUpdate = false;
128
+ } else if (type === "clear" && currentMxLike) {
129
+ applyingParentUpdate = true;
130
+ currentMxLike.history = [];
131
+ currentMxLike.indexOfNextAdd = 0;
132
+ currentMxLike.fireEvent(createMxEventObject("clear"));
133
+ applyingParentUpdate = false;
134
+ }
135
+ };
136
+ ydoc.on("update", onYdocUpdate);
137
+ awareness.on("update", onAwarenessUpdate);
138
+ window.addEventListener("message", onMessage);
139
+ window.parent.postMessage({ type: "init" }, "*");
140
+ return {
141
+ get serverClientId() {
142
+ return serverClientId;
143
+ },
144
+ takeoverUndoManager(file) {
145
+ if (currentCleanup) {
146
+ currentCleanup();
147
+ }
148
+ const editor = file.getUi().editor;
149
+ const originUndoManager = editor.undoManager;
150
+ const pairs = [];
151
+ const raw = Array.isArray(originUndoManager == null ? void 0 : originUndoManager.eventListeners) ? originUndoManager.eventListeners : [];
152
+ for (let i = 0; i + 1 < raw.length; i += 2) {
153
+ const key = String(raw[i]);
154
+ const fn = raw[i + 1];
155
+ pairs.push([key, fn]);
156
+ }
157
+ const mxLike = {
158
+ eventListeners: [],
159
+ history: [],
160
+ indexOfNextAdd: 0,
161
+ addListener(name, fn) {
162
+ this.eventListeners.push(name, fn);
163
+ },
164
+ fireEvent(evt) {
165
+ var _a;
166
+ const eventName = (evt == null ? void 0 : evt.name) || (((_a = evt == null ? void 0 : evt.getName) == null ? void 0 : _a.call(evt)) ?? "");
167
+ for (let i = 0; i + 1 < this.eventListeners.length; i += 2) {
168
+ const key = this.eventListeners[i];
169
+ const listener = this.eventListeners[i + 1];
170
+ if (key === eventName) {
171
+ try {
172
+ listener(this, evt);
173
+ } catch (e) {
174
+ console.warn("[iframe-bridge] undoManager event listener error:", e);
175
+ }
176
+ }
177
+ }
178
+ },
179
+ canUndo() {
180
+ return this.indexOfNextAdd > 0;
181
+ },
182
+ canRedo() {
183
+ return this.indexOfNextAdd < this.history.length;
184
+ },
185
+ undo() {
186
+ if (!applyingParentUpdate) {
187
+ window.parent.postMessage({ type: "undo" }, "*");
188
+ }
189
+ },
190
+ redo() {
191
+ if (!applyingParentUpdate) {
192
+ window.parent.postMessage({ type: "redo" }, "*");
193
+ }
194
+ },
195
+ undoableEditHappened() {
196
+ }
197
+ };
198
+ pairs.forEach(([key, fn]) => {
199
+ const k = key.toLowerCase();
200
+ if (k === "add" || k === "clear" || k === "undo" || k === "redo") {
201
+ mxLike.addListener(k, fn);
202
+ }
203
+ });
204
+ currentMxLike = mxLike;
205
+ editor.undoManager = mxLike;
206
+ editor.undoListener = function() {
207
+ };
208
+ const cleanup = () => {
209
+ editor.undoManager = originUndoManager;
210
+ editor.undoListener = originUndoManager == null ? void 0 : originUndoManager.undoListener;
211
+ currentMxLike = null;
212
+ };
213
+ currentCleanup = cleanup;
214
+ return cleanup;
215
+ },
216
+ destroy: () => {
217
+ ydoc.off("update", onYdocUpdate);
218
+ awareness.off("update", onAwarenessUpdate);
219
+ window.removeEventListener("message", onMessage);
220
+ if (currentCleanup) {
221
+ currentCleanup();
222
+ }
223
+ }
224
+ };
225
+ }
226
+ export {
227
+ createIframeBridgeProvider
228
+ };
229
+ //# sourceMappingURL=provider.es.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provider.es.js","sources":["../../../iframe-bridge/src/provider.ts"],"sourcesContent":["import * as Y from \"yjs\";\nimport {\n Awareness,\n applyAwarenessUpdate,\n encodeAwarenessUpdate,\n} from \"y-protocols/awareness\";\n\ntype ListenerFn = (sender: unknown, evt?: unknown) => void;\n\nfunction createMxEventObject(name: string, props?: Record<string, unknown>) {\n const _props = props || {};\n return {\n name,\n getName: () => name,\n getProperty: (k: string) => _props[k],\n };\n}\n\ntype MxLike = Record<string, unknown> & {\n eventListeners: Array<string | ListenerFn>;\n history: unknown[];\n indexOfNextAdd: number;\n addListener(name: string, fn: ListenerFn): void;\n fireEvent(evt: unknown): void;\n canUndo(): boolean;\n canRedo(): boolean;\n undo(): void;\n redo(): void;\n undoableEditHappened(_edit: unknown): void;\n};\n\nexport interface DrawioEditor {\n undoManager?: {\n eventListeners?: unknown[];\n [key: string]: unknown;\n };\n undoListener?: (...args: unknown[]) => void;\n}\n\nexport interface DrawioFile {\n getUi(): { editor: DrawioEditor };\n}\n\nexport interface IframeBridgeProvider {\n serverClientId: number | null;\n takeoverUndoManager: (file: DrawioFile) => () => void;\n destroy: () => void;\n}\n\nfunction readVarUint(data: Uint8Array, pos: number): [number, number] {\n let result = 0;\n let shift = 0;\n let byte: number;\n do {\n byte = data[pos++];\n result |= (byte & 0x7f) << shift;\n shift += 7;\n } while (byte >= 0x80);\n return [result >>> 0, pos];\n}\n\nfunction writeVarUint(value: number): number[] {\n const bytes: number[] = [];\n while (value > 0x7f) {\n bytes.push((value & 0x7f) | 0x80);\n value >>>= 7;\n }\n bytes.push(value);\n return bytes;\n}\n\nfunction readVarString(data: Uint8Array, pos: number): [string, number] {\n const [len, pos2] = readVarUint(data, pos);\n const str = new TextDecoder().decode(data.subarray(pos2, pos2 + len));\n return [str, pos2 + len];\n}\n\nfunction writeVarString(str: string): number[] {\n const encoded = new TextEncoder().encode(str);\n return [...writeVarUint(encoded.length), ...encoded];\n}\n\nfunction remapClientIdInUpdate(\n update: Uint8Array,\n fromId: number,\n toId: number,\n): Uint8Array {\n const result: number[] = [];\n let pos = 0;\n\n const [count, pos2] = readVarUint(update, pos);\n pos = pos2;\n result.push(...writeVarUint(count));\n\n for (let i = 0; i < count; i++) {\n const [clientID, pos3] = readVarUint(update, pos);\n pos = pos3;\n const [clock, pos4] = readVarUint(update, pos);\n pos = pos4;\n const [state, pos5] = readVarString(update, pos);\n pos = pos5;\n\n const mappedId = clientID === fromId ? toId : clientID;\n result.push(...writeVarUint(mappedId));\n result.push(...writeVarUint(clock));\n result.push(...writeVarString(state));\n }\n\n return new Uint8Array(result);\n}\n\nexport function createIframeBridgeProvider(\n ydoc: Y.Doc,\n awareness: Awareness,\n): IframeBridgeProvider {\n let applyingParentUpdate = false;\n let serverClientId: number | null = null;\n let currentCleanup: (() => void) | null = null;\n let currentMxLike: MxLike | null = null;\n\n const onYdocUpdate = (update: Uint8Array) => {\n if (applyingParentUpdate) return;\n // 本地编辑,添加 history 条目\n if (currentMxLike) {\n if (currentMxLike.indexOfNextAdd < currentMxLike.history.length) {\n currentMxLike.history.splice(\n currentMxLike.indexOfNextAdd,\n currentMxLike.history.length - currentMxLike.indexOfNextAdd,\n );\n }\n currentMxLike.history.push({});\n currentMxLike.indexOfNextAdd = currentMxLike.history.length;\n currentMxLike.fireEvent(createMxEventObject(\"add\", { edit: { changes: [] } }));\n }\n window.parent.postMessage(\n { type: \"ydoc-update\", payload: Array.from(update) },\n \"*\",\n );\n };\n\n const onAwarenessUpdate = ({\n added,\n updated,\n removed,\n }: {\n added: number[];\n updated: number[];\n removed: number[];\n }) => {\n if (applyingParentUpdate) return;\n const changes = [...added, ...updated, ...removed];\n if (changes.length === 0) return;\n\n const update = encodeAwarenessUpdate(awareness, changes);\n const remapped =\n serverClientId != null\n ? remapClientIdInUpdate(update, awareness.clientID, serverClientId)\n : update;\n\n window.parent.postMessage(\n { type: \"awareness-update\", payload: Array.from(remapped) },\n \"*\",\n );\n };\n\n const onMessage = (event: MessageEvent) => {\n if (event.source !== window.parent) return;\n const { type, payload, serverClientId: receivedServerId } = event.data;\n\n if (type === \"pong\" && receivedServerId != null) {\n serverClientId = receivedServerId;\n return;\n }\n\n if (type === \"ydoc-sync\" || type === \"ydoc-update\") {\n applyingParentUpdate = true;\n Y.applyUpdate(ydoc, new Uint8Array(payload));\n applyingParentUpdate = false;\n } else if (type === \"awareness-sync\" || type === \"awareness-update\") {\n if (receivedServerId != null) {\n serverClientId = receivedServerId;\n }\n\n const raw = new Uint8Array(payload);\n const remapped =\n serverClientId != null\n ? remapClientIdInUpdate(raw, serverClientId, awareness.clientID)\n : raw;\n\n applyingParentUpdate = true;\n applyAwarenessUpdate(awareness, remapped, null);\n applyingParentUpdate = false;\n } else if (type === \"undo\" && currentMxLike) {\n applyingParentUpdate = true;\n if (currentMxLike.indexOfNextAdd > 0) currentMxLike.indexOfNextAdd--;\n currentMxLike.fireEvent(createMxEventObject(\"undo\", { edit: { changes: [] } }));\n applyingParentUpdate = false;\n } else if (type === \"redo\" && currentMxLike) {\n applyingParentUpdate = true;\n if (currentMxLike.indexOfNextAdd < currentMxLike.history.length) currentMxLike.indexOfNextAdd++;\n currentMxLike.fireEvent(createMxEventObject(\"redo\", { edit: { changes: [] } }));\n applyingParentUpdate = false;\n } else if (type === \"clear\" && currentMxLike) {\n applyingParentUpdate = true;\n currentMxLike.history = [];\n currentMxLike.indexOfNextAdd = 0;\n currentMxLike.fireEvent(createMxEventObject(\"clear\"));\n applyingParentUpdate = false;\n }\n };\n\n ydoc.on(\"update\", onYdocUpdate);\n awareness.on(\"update\", onAwarenessUpdate);\n window.addEventListener(\"message\", onMessage);\n\n window.parent.postMessage({ type: \"init\" }, \"*\");\n\n return {\n get serverClientId() {\n return serverClientId;\n },\n takeoverUndoManager(file: DrawioFile) {\n if (currentCleanup) {\n currentCleanup();\n }\n\n const editor = file.getUi().editor;\n const originUndoManager = editor.undoManager;\n\n const pairs: Array<[string, ListenerFn]> = [];\n const raw = Array.isArray(originUndoManager?.eventListeners)\n ? (originUndoManager.eventListeners as unknown[])\n : [];\n for (let i = 0; i + 1 < raw.length; i += 2) {\n const key = String(raw[i]);\n const fn = raw[i + 1] as ListenerFn;\n pairs.push([key, fn]);\n }\n\n const mxLike: MxLike = {\n eventListeners: [] as Array<string | ListenerFn>,\n history: [] as unknown[],\n indexOfNextAdd: 0,\n\n addListener(name: string, fn: ListenerFn) {\n this.eventListeners.push(name, fn);\n },\n\n fireEvent(evt: unknown) {\n const eventName: string =\n (evt as { name?: string } | undefined)?.name ||\n ((evt as { getName?: () => string } | undefined)?.getName?.() ?? \"\");\n for (let i = 0; i + 1 < this.eventListeners.length; i += 2) {\n const key = this.eventListeners[i];\n const listener = this.eventListeners[i + 1] as ListenerFn;\n if (key === eventName) {\n try {\n listener(this, evt);\n } catch (e) {\n console.warn(\"[iframe-bridge] undoManager event listener error:\", e);\n }\n }\n }\n },\n\n canUndo(): boolean {\n return this.indexOfNextAdd > 0;\n },\n\n canRedo(): boolean {\n return this.indexOfNextAdd < this.history.length;\n },\n\n undo() {\n if (!applyingParentUpdate) {\n window.parent.postMessage({ type: \"undo\" }, \"*\");\n }\n },\n\n redo() {\n if (!applyingParentUpdate) {\n window.parent.postMessage({ type: \"redo\" }, \"*\");\n }\n },\n\n undoableEditHappened() {\n // no-op\n },\n };\n\n pairs.forEach(([key, fn]) => {\n const k = key.toLowerCase();\n if (k === \"add\" || k === \"clear\" || k === \"undo\" || k === \"redo\") {\n mxLike.addListener(k, fn);\n }\n });\n\n currentMxLike = mxLike;\n editor.undoManager = mxLike as any;\n editor.undoListener = function () {};\n\n const cleanup = () => {\n editor.undoManager = originUndoManager;\n editor.undoListener = originUndoManager?.undoListener as ((...args: unknown[]) => void) | undefined;\n currentMxLike = null;\n };\n\n currentCleanup = cleanup;\n return cleanup;\n },\n destroy: () => {\n ydoc.off(\"update\", onYdocUpdate);\n awareness.off(\"update\", onAwarenessUpdate);\n window.removeEventListener(\"message\", onMessage);\n if (currentCleanup) {\n currentCleanup();\n }\n },\n };\n}\n"],"names":[],"mappings":"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"}