y-mxgraph 0.6.14 → 0.7.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.md +195 -39
- package/README.zh-CN.md +195 -38
- package/iframe-bridge/provider.cjs +149 -63
- package/iframe-bridge/provider.cjs.map +1 -1
- package/iframe-bridge/provider.d.ts +1 -1
- package/iframe-bridge/provider.d.ts.map +1 -1
- package/iframe-bridge/provider.js +149 -63
- package/iframe-bridge/provider.js.map +1 -1
- package/package.json +1 -1
|
@@ -85,8 +85,29 @@ function remapClientIdInUpdate(update, fromId, toId) {
|
|
|
85
85
|
}
|
|
86
86
|
return new Uint8Array(result);
|
|
87
87
|
}
|
|
88
|
-
function
|
|
89
|
-
const
|
|
88
|
+
function parseAwarenessPayload(data) {
|
|
89
|
+
const result = /* @__PURE__ */ new Map();
|
|
90
|
+
let pos = 0;
|
|
91
|
+
const [count, pos2] = readVarUint(data, pos);
|
|
92
|
+
pos = pos2;
|
|
93
|
+
for (let i = 0; i < count; i++) {
|
|
94
|
+
const [clientID, pos3] = readVarUint(data, pos);
|
|
95
|
+
pos = pos3;
|
|
96
|
+
const [clock, pos4] = readVarUint(data, pos);
|
|
97
|
+
pos = pos4;
|
|
98
|
+
const [stateStr, pos5] = readVarString(data, pos);
|
|
99
|
+
pos = pos5;
|
|
100
|
+
if (stateStr) {
|
|
101
|
+
try {
|
|
102
|
+
result.set(clientID, JSON.parse(stateStr));
|
|
103
|
+
} catch (e) {
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return result;
|
|
108
|
+
}
|
|
109
|
+
function createIframeBridgeProvider(ydoc, options) {
|
|
110
|
+
const { awareness: externalAwareness, debug = false } = options != null ? options : {};
|
|
90
111
|
let applyingParentUpdate = false;
|
|
91
112
|
let serverClientId = null;
|
|
92
113
|
let currentCleanup = null;
|
|
@@ -96,6 +117,81 @@ function createIframeBridgeProvider(ydoc, awareness, options) {
|
|
|
96
117
|
const connectListeners = /* @__PURE__ */ new Set();
|
|
97
118
|
const disconnectListeners = /* @__PURE__ */ new Set();
|
|
98
119
|
const log = debug ? (...args) => console.log("[iframe-bridge provider]", ...args) : () => void 0;
|
|
120
|
+
const useExternalAwareness = !!externalAwareness;
|
|
121
|
+
let awareness;
|
|
122
|
+
const localStates = /* @__PURE__ */ new Map();
|
|
123
|
+
let localClientId = Math.floor(Math.random() * 2147483647) + 1;
|
|
124
|
+
const updateHandlers = /* @__PURE__ */ new Set();
|
|
125
|
+
function createAwarenessLike() {
|
|
126
|
+
let pendingState = void 0;
|
|
127
|
+
let flushTimer = null;
|
|
128
|
+
const FLUSH_INTERVAL = 50;
|
|
129
|
+
function getEffectiveClientId() {
|
|
130
|
+
return serverClientId != null ? serverClientId : localClientId;
|
|
131
|
+
}
|
|
132
|
+
function scheduleFlush() {
|
|
133
|
+
if (flushTimer) return;
|
|
134
|
+
flushTimer = setTimeout(() => {
|
|
135
|
+
flushTimer = null;
|
|
136
|
+
if (pendingState !== void 0 && connected) {
|
|
137
|
+
window.parent.postMessage({ type: "awareness-local-state", state: pendingState }, "*");
|
|
138
|
+
pendingState = void 0;
|
|
139
|
+
}
|
|
140
|
+
}, FLUSH_INTERVAL);
|
|
141
|
+
}
|
|
142
|
+
return {
|
|
143
|
+
get clientID() {
|
|
144
|
+
return getEffectiveClientId();
|
|
145
|
+
},
|
|
146
|
+
get states() {
|
|
147
|
+
return localStates;
|
|
148
|
+
},
|
|
149
|
+
getStates() {
|
|
150
|
+
return new Map(localStates);
|
|
151
|
+
},
|
|
152
|
+
getLocalState() {
|
|
153
|
+
var _a;
|
|
154
|
+
return (_a = localStates.get(getEffectiveClientId())) != null ? _a : null;
|
|
155
|
+
},
|
|
156
|
+
setLocalState(state) {
|
|
157
|
+
const id = getEffectiveClientId();
|
|
158
|
+
if (state === null) {
|
|
159
|
+
localStates.delete(id);
|
|
160
|
+
} else {
|
|
161
|
+
localStates.set(id, state);
|
|
162
|
+
}
|
|
163
|
+
pendingState = state;
|
|
164
|
+
scheduleFlush();
|
|
165
|
+
const update = { added: state && !localStates.has(id) ? [id] : [], updated: state ? [id] : [], removed: state === null ? [id] : [] };
|
|
166
|
+
updateHandlers.forEach((handler) => handler(update));
|
|
167
|
+
},
|
|
168
|
+
setLocalStateField(field, value) {
|
|
169
|
+
const id = getEffectiveClientId();
|
|
170
|
+
const current = localStates.get(id) || {};
|
|
171
|
+
const newState = __spreadProps(__spreadValues({}, current), { [field]: value });
|
|
172
|
+
localStates.set(id, newState);
|
|
173
|
+
pendingState = newState;
|
|
174
|
+
scheduleFlush();
|
|
175
|
+
const update = { added: [], updated: [id], removed: [] };
|
|
176
|
+
updateHandlers.forEach((handler) => handler(update));
|
|
177
|
+
},
|
|
178
|
+
on(event, handler) {
|
|
179
|
+
if (event === "update") {
|
|
180
|
+
updateHandlers.add(handler);
|
|
181
|
+
}
|
|
182
|
+
},
|
|
183
|
+
off(event, handler) {
|
|
184
|
+
if (event === "update") {
|
|
185
|
+
updateHandlers.delete(handler);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
if (externalAwareness) {
|
|
191
|
+
awareness = externalAwareness;
|
|
192
|
+
} else {
|
|
193
|
+
awareness = createAwarenessLike();
|
|
194
|
+
}
|
|
99
195
|
function formatPayload(payload) {
|
|
100
196
|
if (payload instanceof Uint8Array) {
|
|
101
197
|
return { bytes: payload.byteLength };
|
|
@@ -147,25 +243,17 @@ function createIframeBridgeProvider(ydoc, awareness, options) {
|
|
|
147
243
|
removed
|
|
148
244
|
}) => {
|
|
149
245
|
if (applyingParentUpdate) {
|
|
150
|
-
log("[DEBUG] onAwarenessUpdate: skipped (applyingParentUpdate)");
|
|
151
246
|
return;
|
|
152
247
|
}
|
|
153
248
|
if (!connected) {
|
|
154
|
-
log("[DEBUG] onAwarenessUpdate: skipped (not connected)");
|
|
155
249
|
return;
|
|
156
250
|
}
|
|
157
251
|
const changes = [...added, ...updated, ...removed];
|
|
158
252
|
if (changes.length === 0) return;
|
|
159
|
-
const
|
|
160
|
-
const localChanged = changes.includes(
|
|
253
|
+
const localClientId2 = awareness.clientID;
|
|
254
|
+
const localChanged = changes.includes(localClientId2);
|
|
161
255
|
if (!localChanged) return;
|
|
162
256
|
const state = awareness.getLocalState();
|
|
163
|
-
log("[DEBUG] onAwarenessUpdate: sending local state", {
|
|
164
|
-
localClientId,
|
|
165
|
-
state,
|
|
166
|
-
connected,
|
|
167
|
-
applyingParentUpdate
|
|
168
|
-
});
|
|
169
257
|
const message = { type: "awareness-local-state", state };
|
|
170
258
|
logMessage("send", "awareness-local-state", state);
|
|
171
259
|
window.parent.postMessage(message, "*");
|
|
@@ -191,61 +279,54 @@ function createIframeBridgeProvider(ydoc, awareness, options) {
|
|
|
191
279
|
}
|
|
192
280
|
} else if (type === "awareness-sync" || type === "awareness-update") {
|
|
193
281
|
logMessage("recv", type, payload);
|
|
282
|
+
const prevLocalId = localClientId;
|
|
194
283
|
if (receivedServerId != null) {
|
|
195
284
|
serverClientId = receivedServerId;
|
|
285
|
+
if (useExternalAwareness === false && receivedServerId != null && prevLocalId !== receivedServerId) {
|
|
286
|
+
const tempState = localStates.get(prevLocalId);
|
|
287
|
+
if (tempState) {
|
|
288
|
+
localStates.delete(prevLocalId);
|
|
289
|
+
localStates.set(receivedServerId, tempState);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
196
292
|
}
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
serverId
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
new Uint8Array(payload),
|
|
214
|
-
serverId,
|
|
215
|
-
localClientId
|
|
216
|
-
);
|
|
217
|
-
log("[DEBUG] awareness-sync/update: applying remapped update", {
|
|
218
|
-
serverId,
|
|
219
|
-
localClientId,
|
|
220
|
-
remappedLength: remapped.length,
|
|
221
|
-
beforeState: awareness.getLocalState(),
|
|
222
|
-
beforeStates: Object.fromEntries(awareness.getStates())
|
|
223
|
-
});
|
|
224
|
-
if (type === "awareness-sync") {
|
|
225
|
-
log("[DEBUG] awareness-sync: clearing local meta before apply");
|
|
226
|
-
awareness.meta.delete(localClientId);
|
|
227
|
-
awareness.setLocalState(null);
|
|
293
|
+
if (useExternalAwareness) {
|
|
294
|
+
const serverId = receivedServerId != null ? receivedServerId : serverClientId;
|
|
295
|
+
const localId = awareness.clientID;
|
|
296
|
+
applyingParentUpdate = true;
|
|
297
|
+
if (serverId != null && serverId !== localId) {
|
|
298
|
+
const remapped = remapClientIdInUpdate(
|
|
299
|
+
new Uint8Array(payload),
|
|
300
|
+
serverId,
|
|
301
|
+
localId
|
|
302
|
+
);
|
|
303
|
+
if (type === "awareness-sync") {
|
|
304
|
+
awareness.meta.delete(localId);
|
|
305
|
+
awareness.setLocalState(null);
|
|
306
|
+
}
|
|
307
|
+
applyAwarenessUpdate(awareness, remapped, null);
|
|
308
|
+
} else {
|
|
309
|
+
applyAwarenessUpdate(awareness, new Uint8Array(payload), null);
|
|
228
310
|
}
|
|
229
|
-
|
|
230
|
-
log("[DEBUG] awareness-sync/update: AFTER remapped apply", {
|
|
231
|
-
afterState: awareness.getLocalState(),
|
|
232
|
-
afterStates: Object.fromEntries(awareness.getStates())
|
|
233
|
-
});
|
|
311
|
+
applyingParentUpdate = false;
|
|
234
312
|
} else {
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
313
|
+
const parsedStates = parseAwarenessPayload(new Uint8Array(payload));
|
|
314
|
+
applyingParentUpdate = true;
|
|
315
|
+
const changedClientIds = [];
|
|
316
|
+
for (const [id, state] of parsedStates) {
|
|
317
|
+
const existed = localStates.has(id);
|
|
318
|
+
const changed = !existed || JSON.stringify(localStates.get(id)) !== JSON.stringify(state);
|
|
319
|
+
localStates.set(id, state);
|
|
320
|
+
if (changed) {
|
|
321
|
+
changedClientIds.push(id);
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
if (changedClientIds.length > 0) {
|
|
325
|
+
const update = { added: [], updated: changedClientIds, removed: [] };
|
|
326
|
+
updateHandlers.forEach((handler) => handler(update));
|
|
327
|
+
}
|
|
328
|
+
applyingParentUpdate = false;
|
|
242
329
|
}
|
|
243
|
-
log("[DEBUG] awareness-sync/update: final state", {
|
|
244
|
-
type,
|
|
245
|
-
localState: awareness.getLocalState(),
|
|
246
|
-
allStates: Object.fromEntries(awareness.getStates())
|
|
247
|
-
});
|
|
248
|
-
applyingParentUpdate = false;
|
|
249
330
|
} else if (type === "undo-state" && currentMxLike) {
|
|
250
331
|
const { undoStackSize, redoStackSize } = event.data;
|
|
251
332
|
const oldIndex = currentMxLike.indexOfNextAdd;
|
|
@@ -289,6 +370,9 @@ function createIframeBridgeProvider(ydoc, awareness, options) {
|
|
|
289
370
|
get connected() {
|
|
290
371
|
return connected;
|
|
291
372
|
},
|
|
373
|
+
get awareness() {
|
|
374
|
+
return awareness;
|
|
375
|
+
},
|
|
292
376
|
onConnect(fn) {
|
|
293
377
|
connectListeners.add(fn);
|
|
294
378
|
return () => connectListeners.delete(fn);
|
|
@@ -313,7 +397,7 @@ function createIframeBridgeProvider(ydoc, awareness, options) {
|
|
|
313
397
|
awareness.setLocalState(__spreadProps(__spreadValues({}, currentLocal), {
|
|
314
398
|
user: newUser
|
|
315
399
|
}));
|
|
316
|
-
if (connected) {
|
|
400
|
+
if (useExternalAwareness && connected) {
|
|
317
401
|
const message = { type: "set-local-fields", fields };
|
|
318
402
|
logMessage("send", "set-local-fields", fields);
|
|
319
403
|
window.parent.postMessage(message, "*");
|
|
@@ -396,7 +480,9 @@ function createIframeBridgeProvider(ydoc, awareness, options) {
|
|
|
396
480
|
},
|
|
397
481
|
destroy: () => {
|
|
398
482
|
ydoc.off("update", onYdocUpdate);
|
|
399
|
-
|
|
483
|
+
if (useExternalAwareness) {
|
|
484
|
+
awareness.off("update", onAwarenessUpdate);
|
|
485
|
+
}
|
|
400
486
|
window.removeEventListener("message", onMessage);
|
|
401
487
|
if (initRetryTimer) {
|
|
402
488
|
clearInterval(initRetryTimer);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"provider.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 IframeBridgeProviderOptions {\n debug?: boolean;\n}\n\nexport interface IframeBridgeProvider {\n serverClientId: number | null;\n connected: boolean;\n onConnect: (fn: () => void) => () => void;\n onDisconnect: (fn: () => void) => () => void;\n on: (event: \"connect\" | \"disconnect\", fn: () => void) => () => void;\n setLocalFields: (fields: Record<string, unknown>) => void;\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 entries: Array<{ clientID: number; clock: number; state: string }> = [];\n const seenClientIds = new Set<number>();\n let pos = 0;\n\n const [count, pos2] = readVarUint(update, pos);\n pos = pos2;\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 if (seenClientIds.has(mappedId)) {\n continue;\n }\n\n seenClientIds.add(mappedId);\n entries.push({ clientID: mappedId, clock, state });\n }\n\n const result: number[] = [];\n result.push(...writeVarUint(entries.length));\n for (const entry of entries) {\n result.push(...writeVarUint(entry.clientID));\n result.push(...writeVarUint(entry.clock));\n result.push(...writeVarString(entry.state));\n }\n\n return new Uint8Array(result);\n}\n\nexport function createIframeBridgeProvider(\n ydoc: Y.Doc,\n awareness: Awareness,\n options?: IframeBridgeProviderOptions,\n): IframeBridgeProvider {\n const { debug = false } = options ?? {};\n let applyingParentUpdate = false;\n let serverClientId: number | null = null;\n let currentCleanup: (() => void) | null = null;\n let currentMxLike: MxLike | null = null;\n let connected = false;\n let initRetryTimer: ReturnType<typeof setInterval> | null = null;\n const connectListeners = new Set<() => void>();\n const disconnectListeners = new Set<() => void>();\n\n const log = debug\n ? (...args: unknown[]) => console.log(\"[iframe-bridge provider]\", ...args)\n : () => undefined;\n\n function formatPayload(payload: unknown) {\n if (payload instanceof Uint8Array) {\n return { bytes: payload.byteLength };\n }\n if (Array.isArray(payload) && payload.every((item) => typeof item === \"number\")) {\n return { bytes: payload.length };\n }\n return payload;\n }\n\n function logMessage(direction: \"send\" | \"recv\", type: string, payload?: unknown) {\n if (!debug) return;\n log(direction, type, formatPayload(payload));\n }\n\n function parentPostMessage(message: unknown) {\n logMessage(\"send\", (message as { type?: string }).type ?? \"postMessage\", message);\n window.parent.postMessage(message, \"*\");\n }\n\n function setConnected(value: boolean) {\n if (connected === value) return;\n connected = value;\n if (value) {\n connectListeners.forEach((fn) => fn());\n } else {\n disconnectListeners.forEach((fn) => fn());\n }\n }\n\n function startInitRetry() {\n if (initRetryTimer) {\n clearInterval(initRetryTimer);\n }\n parentPostMessage({ type: \"init\" });\n initRetryTimer = setInterval(() => {\n if (!connected) {\n parentPostMessage({ type: \"init\" });\n }\n }, 1000);\n }\n\n const onYdocUpdate = (update: Uint8Array, origin: unknown) => {\n if (applyingParentUpdate) return;\n // 检测基线数据:origin 为 null 时是 xml2ydoc 首次初始化\n const isBaseline = origin === null || origin === undefined;\n const message = { type: \"ydoc-update\", payload: Array.from(update), isBaseline };\n logMessage(\"send\", \"ydoc-update\", message);\n window.parent.postMessage(message, \"*\");\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) {\n log(\"[DEBUG] onAwarenessUpdate: skipped (applyingParentUpdate)\");\n return;\n }\n // 未连接时不推送本地状态到 server,避免随机值覆盖 server 数据\n if (!connected) {\n log(\"[DEBUG] onAwarenessUpdate: skipped (not connected)\");\n return;\n }\n const changes = [...added, ...updated, ...removed];\n if (changes.length === 0) return;\n\n // 只同步本地 clientID 的状态给父页面\n // 其他 peers 的更新由父页面的 WebRTC/WebSocket provider 负责广播\n const localClientId = awareness.clientID;\n const localChanged = changes.includes(localClientId);\n if (!localChanged) return;\n\n const state = awareness.getLocalState();\n log(\"[DEBUG] onAwarenessUpdate: sending local state\", {\n localClientId,\n state,\n connected,\n applyingParentUpdate,\n });\n const message = { type: \"awareness-local-state\", state };\n logMessage(\"send\", \"awareness-local-state\", state);\n window.parent.postMessage(message, \"*\");\n };\n\n const onMessage = (event: MessageEvent) => {\n if (event.source !== window.parent) return;\n const { type, payload, serverClientId: receivedServerId } = event.data;\n\n logMessage(\"recv\", type, payload);\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 if (type === \"ydoc-sync\" && !connected) {\n setConnected(true);\n if (initRetryTimer) {\n clearInterval(initRetryTimer);\n initRetryTimer = null;\n }\n }\n } else if (type === \"awareness-sync\" || type === \"awareness-update\") {\n logMessage(\"recv\", type, payload);\n if (receivedServerId != null) {\n serverClientId = receivedServerId;\n }\n\n const serverId = receivedServerId ?? serverClientId;\n const localClientId = awareness.clientID;\n \n log(\"[DEBUG] awareness-sync/update: received message\", {\n type,\n serverId,\n localClientId,\n payloadLength: payload?.length,\n payloadSample: payload?.slice(0, 20),\n });\n \n log(\"[DEBUG] awareness-sync/update: before apply\", {\n currentState: awareness.getLocalState(),\n currentStates: Object.fromEntries(awareness.getStates()),\n });\n \n applyingParentUpdate = true;\n if (serverId != null && serverId !== localClientId) {\n const remapped = remapClientIdInUpdate(\n new Uint8Array(payload),\n serverId,\n localClientId,\n );\n log(\"[DEBUG] awareness-sync/update: applying remapped update\", {\n serverId,\n localClientId,\n remappedLength: remapped.length,\n beforeState: awareness.getLocalState(),\n beforeStates: Object.fromEntries(awareness.getStates()),\n });\n \n // awareness-sync 时先清除本地状态的 meta,确保 server 的状态能覆盖\n // 因为 bindCollaborator 可能已经设置了随机值,导致本地 clock 更大\n if (type === \"awareness-sync\") {\n log(\"[DEBUG] awareness-sync: clearing local meta before apply\");\n // 直接删除本地 clientID 的 meta,这样 applyAwarenessUpdate 会认为本地状态是全新的\n (awareness as any).meta.delete(localClientId);\n awareness.setLocalState(null);\n }\n \n applyAwarenessUpdate(awareness, remapped, null);\n log(\"[DEBUG] awareness-sync/update: AFTER remapped apply\", {\n afterState: awareness.getLocalState(),\n afterStates: Object.fromEntries(awareness.getStates()),\n });\n } else {\n log(\"[DEBUG] awareness-sync/update: applying direct update\", {\n beforeState: awareness.getLocalState(),\n });\n applyAwarenessUpdate(awareness, new Uint8Array(payload), null);\n log(\"[DEBUG] awareness-sync/update: AFTER direct apply\", {\n afterState: awareness.getLocalState(),\n });\n }\n\n log(\"[DEBUG] awareness-sync/update: final state\", {\n type,\n localState: awareness.getLocalState(),\n allStates: Object.fromEntries(awareness.getStates()),\n });\n\n // awareness-sync 时直接接受 server 的值,不合并本地值\n // 避免本地随机值覆盖 server 数据\n applyingParentUpdate = false;\n } else if (type === \"undo-state\" && currentMxLike) {\n // 从 Server 同步真实的 undo/redo 状态\n const { undoStackSize, redoStackSize } = event.data;\n\n const oldIndex = currentMxLike.indexOfNextAdd;\n const newIndex = undoStackSize || 0;\n const newTotal = (undoStackSize || 0) + (redoStackSize || 0);\n\n // 直接根据 server 状态重建本地状态\n applyingParentUpdate = true;\n\n // 重建 history 数组匹配 server 的总大小\n currentMxLike.history = new Array(newTotal).fill({});\n currentMxLike.indexOfNextAdd = newIndex;\n\n // 触发对应事件通知 UI 更新\n if (newTotal === 0) {\n currentMxLike.fireEvent(createMxEventObject(\"clear\"));\n } else if (newIndex < oldIndex) {\n currentMxLike.fireEvent(\n createMxEventObject(\"undo\", { edit: { changes: [] } }),\n );\n } else if (newIndex > oldIndex) {\n currentMxLike.fireEvent(\n createMxEventObject(\"redo\", { edit: { changes: [] } }),\n );\n } else {\n currentMxLike.fireEvent(\n createMxEventObject(\"add\", { edit: { changes: [] } }),\n );\n }\n\n applyingParentUpdate = false;\n } else if (type === \"disconnect\") {\n setConnected(false);\n startInitRetry();\n }\n };\n\n ydoc.on(\"update\", onYdocUpdate);\n awareness.on(\"update\", onAwarenessUpdate);\n window.addEventListener(\"message\", onMessage);\n\n startInitRetry();\n\n // 发送 ping 获取 serverClientId\n setTimeout(() => {\n window.parent.postMessage({ type: \"ping\" }, \"*\");\n }, 100);\n\n return {\n get serverClientId() {\n return serverClientId;\n },\n get connected() {\n return connected;\n },\n onConnect(fn: () => void) {\n connectListeners.add(fn);\n return () => connectListeners.delete(fn);\n },\n onDisconnect(fn: () => void) {\n disconnectListeners.add(fn);\n return () => disconnectListeners.delete(fn);\n },\n on(event: \"connect\" | \"disconnect\", fn: () => void) {\n if (event === \"connect\") {\n connectListeners.add(fn);\n return () => connectListeners.delete(fn);\n } else {\n disconnectListeners.add(fn);\n return () => disconnectListeners.delete(fn);\n }\n },\n setLocalFields(fields: Record<string, unknown>) {\n const currentLocal = awareness.getLocalState() || {};\n const currentUser = (currentLocal as { user?: Record<string, unknown> }).user || {};\n const newUser = { ...currentUser, ...fields };\n awareness.setLocalState({\n ...currentLocal,\n user: newUser,\n });\n // 未连接时只设置本地状态,连接后由 server 同步\n if (connected) {\n const message = { type: \"set-local-fields\", fields };\n logMessage(\"send\", \"set-local-fields\", fields);\n window.parent.postMessage(message, \"*\");\n }\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 \"\");\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(\n \"[iframe-bridge] undoManager event listener error:\",\n e,\n );\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\n | ((...args: unknown[]) => void)\n | 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 (initRetryTimer) {\n clearInterval(initRetryTimer);\n initRetryTimer = null;\n }\n connectListeners.clear();\n disconnectListeners.clear();\n if (currentCleanup) {\n currentCleanup();\n }\n },\n };\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"provider.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\n/**\n * Awareness-like 接口,只需要支持本地状态管理。\n * 用于 iframe-bridge provider 不需要与父页面同步 awareness 的场景。\n */\nexport interface AwarenessLike {\n readonly clientID: number;\n readonly states: Map<number, Record<string, unknown>>;\n getStates(): Map<number, Record<string, unknown>>;\n getLocalState(): Record<string, unknown> | null;\n setLocalState(state: Record<string, unknown> | null): void;\n setLocalStateField(field: string, value: unknown): void;\n on(event: \"update\", handler: (update: { added: number[]; updated: number[]; removed: number[] }) => void): void;\n off(event: \"update\", handler: (update: { added: number[]; updated: number[]; removed: number[] }) => void): void;\n}\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 IframeBridgeProviderOptions {\n awareness?: Awareness;\n debug?: boolean;\n}\n\nexport interface IframeBridgeProvider {\n serverClientId: number | null;\n connected: boolean;\n awareness: Awareness;\n onConnect: (fn: () => void) => () => void;\n onDisconnect: (fn: () => void) => () => void;\n on: (event: \"connect\" | \"disconnect\", fn: () => void) => () => void;\n setLocalFields: (fields: Record<string, unknown>) => void;\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 entries: Array<{ clientID: number; clock: number; state: string }> = [];\n const seenClientIds = new Set<number>();\n let pos = 0;\n\n const [count, pos2] = readVarUint(update, pos);\n pos = pos2;\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 if (seenClientIds.has(mappedId)) {\n continue;\n }\n\n seenClientIds.add(mappedId);\n entries.push({ clientID: mappedId, clock, state });\n }\n\n const result: number[] = [];\n result.push(...writeVarUint(entries.length));\n for (const entry of entries) {\n result.push(...writeVarUint(entry.clientID));\n result.push(...writeVarUint(entry.clock));\n result.push(...writeVarString(entry.state));\n }\n\n return new Uint8Array(result);\n}\n\nfunction parseAwarenessPayload(data: Uint8Array): Map<number, Record<string, unknown>> {\n const result = new Map<number, Record<string, unknown>>();\n let pos = 0;\n const [count, pos2] = readVarUint(data, pos);\n pos = pos2;\n\n for (let i = 0; i < count; i++) {\n const [clientID, pos3] = readVarUint(data, pos);\n pos = pos3;\n const [clock, pos4] = readVarUint(data, pos);\n pos = pos4;\n const [stateStr, pos5] = readVarString(data, pos);\n pos = pos5;\n\n if (stateStr) {\n try {\n result.set(clientID, JSON.parse(stateStr));\n } catch {}\n }\n }\n return result;\n}\n\nexport function createIframeBridgeProvider(\n ydoc: Y.Doc,\n options?: IframeBridgeProviderOptions,\n): IframeBridgeProvider {\n const { awareness: externalAwareness, debug = false } = options ?? {};\n let applyingParentUpdate = false;\n let serverClientId: number | null = null;\n let currentCleanup: (() => void) | null = null;\n let currentMxLike: MxLike | null = null;\n let connected = false;\n let initRetryTimer: ReturnType<typeof setInterval> | null = null;\n const connectListeners = new Set<() => void>();\n const disconnectListeners = new Set<() => void>();\n\n const log = debug\n ? (...args: unknown[]) => console.log(\"[iframe-bridge provider]\", ...args)\n : () => undefined;\n\n const useExternalAwareness = !!externalAwareness;\n let awareness: Awareness | AwarenessLike;\n const localStates = new Map<number, Record<string, unknown>>();\n let localClientId = Math.floor(Math.random() * 2147483647) + 1;\n const updateHandlers = new Set<(update: { added: number[]; updated: number[]; removed: number[] }) => void>();\n\n function createAwarenessLike(): AwarenessLike {\n let pendingState: Record<string, unknown> | null | undefined = undefined;\n let flushTimer: ReturnType<typeof setTimeout> | null = null;\n const FLUSH_INTERVAL = 50;\n\n function getEffectiveClientId() {\n return serverClientId ?? localClientId;\n }\n\n function scheduleFlush() {\n if (flushTimer) return;\n flushTimer = setTimeout(() => {\n flushTimer = null;\n if (pendingState !== undefined && connected) {\n window.parent.postMessage({ type: \"awareness-local-state\", state: pendingState }, \"*\");\n pendingState = undefined;\n }\n }, FLUSH_INTERVAL);\n }\n\n return {\n get clientID() {\n return getEffectiveClientId();\n },\n get states() {\n return localStates;\n },\n getStates() {\n return new Map(localStates);\n },\n getLocalState() {\n return localStates.get(getEffectiveClientId()) ?? null;\n },\n setLocalState(state: Record<string, unknown> | null) {\n const id = getEffectiveClientId();\n if (state === null) {\n localStates.delete(id);\n } else {\n localStates.set(id, state);\n }\n pendingState = state;\n scheduleFlush();\n const update = { added: state && !localStates.has(id) ? [id] : [], updated: state ? [id] : [], removed: state === null ? [id] : [] };\n updateHandlers.forEach(handler => handler(update));\n },\n setLocalStateField(field: string, value: unknown) {\n const id = getEffectiveClientId();\n const current = localStates.get(id) || {};\n const newState = { ...current, [field]: value };\n localStates.set(id, newState);\n pendingState = newState;\n scheduleFlush();\n const update = { added: [], updated: [id], removed: [] };\n updateHandlers.forEach(handler => handler(update));\n },\n on(event: \"update\", handler: (update: { added: number[]; updated: number[]; removed: number[] }) => void) {\n if (event === \"update\") {\n updateHandlers.add(handler);\n }\n },\n off(event: \"update\", handler: (update: { added: number[]; updated: number[]; removed: number[] }) => void) {\n if (event === \"update\") {\n updateHandlers.delete(handler);\n }\n },\n };\n }\n\n if (externalAwareness) {\n awareness = externalAwareness;\n } else {\n awareness = createAwarenessLike();\n }\n\n function formatPayload(payload: unknown) {\n if (payload instanceof Uint8Array) {\n return { bytes: payload.byteLength };\n }\n if (Array.isArray(payload) && payload.every((item) => typeof item === \"number\")) {\n return { bytes: payload.length };\n }\n return payload;\n }\n\n function logMessage(direction: \"send\" | \"recv\", type: string, payload?: unknown) {\n if (!debug) return;\n log(direction, type, formatPayload(payload));\n }\n\n function parentPostMessage(message: unknown) {\n logMessage(\"send\", (message as { type?: string }).type ?? \"postMessage\", message);\n window.parent.postMessage(message, \"*\");\n }\n\n function setConnected(value: boolean) {\n if (connected === value) return;\n connected = value;\n if (value) {\n connectListeners.forEach((fn) => fn());\n } else {\n disconnectListeners.forEach((fn) => fn());\n }\n }\n\n function startInitRetry() {\n if (initRetryTimer) {\n clearInterval(initRetryTimer);\n }\n parentPostMessage({ type: \"init\" });\n initRetryTimer = setInterval(() => {\n if (!connected) {\n parentPostMessage({ type: \"init\" });\n }\n }, 1000);\n }\n\n const onYdocUpdate = (update: Uint8Array, origin: unknown) => {\n if (applyingParentUpdate) return;\n // 检测基线数据:origin 为 null 时是 xml2ydoc 首次初始化\n const isBaseline = origin === null || origin === undefined;\n const message = { type: \"ydoc-update\", payload: Array.from(update), isBaseline };\n logMessage(\"send\", \"ydoc-update\", message);\n window.parent.postMessage(message, \"*\");\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) {\n return;\n }\n if (!connected) {\n return;\n }\n const changes = [...added, ...updated, ...removed];\n if (changes.length === 0) return;\n\n const localClientId = awareness.clientID;\n const localChanged = changes.includes(localClientId);\n if (!localChanged) return;\n\n const state = awareness.getLocalState();\n const message = { type: \"awareness-local-state\", state };\n logMessage(\"send\", \"awareness-local-state\", state);\n window.parent.postMessage(message, \"*\");\n };\n\n const onMessage = (event: MessageEvent) => {\n if (event.source !== window.parent) return;\n const { type, payload, serverClientId: receivedServerId } = event.data;\n\n logMessage(\"recv\", type, payload);\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 if (type === \"ydoc-sync\" && !connected) {\n setConnected(true);\n if (initRetryTimer) {\n clearInterval(initRetryTimer);\n initRetryTimer = null;\n }\n }\n } else if (type === \"awareness-sync\" || type === \"awareness-update\") {\n logMessage(\"recv\", type, payload);\n const prevLocalId = localClientId;\n if (receivedServerId != null) {\n serverClientId = receivedServerId;\n if (useExternalAwareness === false && receivedServerId != null && prevLocalId !== receivedServerId) {\n const tempState = localStates.get(prevLocalId);\n if (tempState) {\n localStates.delete(prevLocalId);\n localStates.set(receivedServerId, tempState);\n }\n }\n }\n\n if (useExternalAwareness) {\n const serverId = receivedServerId ?? serverClientId;\n const localId = awareness.clientID;\n \n applyingParentUpdate = true;\n if (serverId != null && serverId !== localId) {\n const remapped = remapClientIdInUpdate(\n new Uint8Array(payload),\n serverId,\n localId,\n );\n \n if (type === \"awareness-sync\") {\n (awareness as Awareness).meta.delete(localId);\n awareness.setLocalState(null);\n }\n \n applyAwarenessUpdate(awareness as Awareness, remapped, null);\n } else {\n applyAwarenessUpdate(awareness as Awareness, new Uint8Array(payload), null);\n }\n applyingParentUpdate = false;\n } else {\n const parsedStates = parseAwarenessPayload(new Uint8Array(payload));\n applyingParentUpdate = true;\n const changedClientIds: number[] = [];\n for (const [id, state] of parsedStates) {\n const existed = localStates.has(id);\n const changed = !existed || JSON.stringify(localStates.get(id)) !== JSON.stringify(state);\n localStates.set(id, state);\n if (changed) {\n changedClientIds.push(id);\n }\n }\n if (changedClientIds.length > 0) {\n const update = { added: [], updated: changedClientIds, removed: [] };\n updateHandlers.forEach(handler => handler(update));\n }\n applyingParentUpdate = false;\n }\n } else if (type === \"undo-state\" && currentMxLike) {\n // 从 Server 同步真实的 undo/redo 状态\n const { undoStackSize, redoStackSize } = event.data;\n\n const oldIndex = currentMxLike.indexOfNextAdd;\n const newIndex = undoStackSize || 0;\n const newTotal = (undoStackSize || 0) + (redoStackSize || 0);\n\n // 直接根据 server 状态重建本地状态\n applyingParentUpdate = true;\n\n // 重建 history 数组匹配 server 的总大小\n currentMxLike.history = new Array(newTotal).fill({});\n currentMxLike.indexOfNextAdd = newIndex;\n\n // 触发对应事件通知 UI 更新\n if (newTotal === 0) {\n currentMxLike.fireEvent(createMxEventObject(\"clear\"));\n } else if (newIndex < oldIndex) {\n currentMxLike.fireEvent(\n createMxEventObject(\"undo\", { edit: { changes: [] } }),\n );\n } else if (newIndex > oldIndex) {\n currentMxLike.fireEvent(\n createMxEventObject(\"redo\", { edit: { changes: [] } }),\n );\n } else {\n currentMxLike.fireEvent(\n createMxEventObject(\"add\", { edit: { changes: [] } }),\n );\n }\n\n applyingParentUpdate = false;\n } else if (type === \"disconnect\") {\n setConnected(false);\n startInitRetry();\n }\n };\n\n ydoc.on(\"update\", onYdocUpdate);\n awareness.on(\"update\", onAwarenessUpdate);\n window.addEventListener(\"message\", onMessage);\n\n startInitRetry();\n\n // 发送 ping 获取 serverClientId\n setTimeout(() => {\n window.parent.postMessage({ type: \"ping\" }, \"*\");\n }, 100);\n\n return {\n get serverClientId() {\n return serverClientId;\n },\n get connected() {\n return connected;\n },\n get awareness() {\n return awareness as Awareness;\n },\n onConnect(fn: () => void) {\n connectListeners.add(fn);\n return () => connectListeners.delete(fn);\n },\n onDisconnect(fn: () => void) {\n disconnectListeners.add(fn);\n return () => disconnectListeners.delete(fn);\n },\n on(event: \"connect\" | \"disconnect\", fn: () => void) {\n if (event === \"connect\") {\n connectListeners.add(fn);\n return () => connectListeners.delete(fn);\n } else {\n disconnectListeners.add(fn);\n return () => disconnectListeners.delete(fn);\n }\n },\n setLocalFields(fields: Record<string, unknown>) {\n const currentLocal = awareness.getLocalState() || {};\n const currentUser = (currentLocal as { user?: Record<string, unknown> }).user || {};\n const newUser = { ...currentUser, ...fields };\n awareness.setLocalState({\n ...currentLocal,\n user: newUser,\n });\n // 未连接时只设置本地状态,连接后由 server 同步\n if (useExternalAwareness && connected) {\n const message = { type: \"set-local-fields\", fields };\n logMessage(\"send\", \"set-local-fields\", fields);\n window.parent.postMessage(message, \"*\");\n }\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 \"\");\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(\n \"[iframe-bridge] undoManager event listener error:\",\n e,\n );\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\n | ((...args: unknown[]) => void)\n | undefined;\n currentMxLike = null;\n };\n\n currentCleanup = cleanup;\n return cleanup;\n },\n destroy: () => {\n ydoc.off(\"update\", onYdocUpdate);\n if (useExternalAwareness) {\n (awareness as Awareness).off(\"update\", onAwarenessUpdate);\n }\n window.removeEventListener(\"message\", onMessage);\n if (initRetryTimer) {\n clearInterval(initRetryTimer);\n initRetryTimer = null;\n }\n connectListeners.clear();\n disconnectListeners.clear();\n if (currentCleanup) {\n currentCleanup();\n }\n },\n };\n}\n"],"names":["localClientId"],"mappings":"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"}
|