y-mxgraph 0.3.2 → 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.
@@ -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"}
@@ -19,6 +19,14 @@ function _interopNamespaceDefault(e) {
19
19
  return Object.freeze(n);
20
20
  }
21
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
+ }
22
30
  function readVarUint(data, pos) {
23
31
  let result = 0;
24
32
  let shift = 0;
@@ -71,8 +79,21 @@ function remapClientIdInUpdate(update, fromId, toId) {
71
79
  function createIframeBridgeProvider(ydoc, awareness$1) {
72
80
  let applyingParentUpdate = false;
73
81
  let serverClientId = null;
82
+ let currentCleanup = null;
83
+ let currentMxLike = null;
74
84
  const onYdocUpdate = (update) => {
75
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
+ }
76
97
  window.parent.postMessage(
77
98
  { type: "ydoc-update", payload: Array.from(update) },
78
99
  "*"
@@ -113,6 +134,22 @@ function createIframeBridgeProvider(ydoc, awareness$1) {
113
134
  applyingParentUpdate = true;
114
135
  awareness.applyAwarenessUpdate(awareness$1, remapped, null);
115
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;
116
153
  }
117
154
  };
118
155
  ydoc.on("update", onYdocUpdate);
@@ -123,10 +160,85 @@ function createIframeBridgeProvider(ydoc, awareness$1) {
123
160
  get serverClientId() {
124
161
  return serverClientId;
125
162
  },
126
- dispose: () => {
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: () => {
127
236
  ydoc.off("update", onYdocUpdate);
128
237
  awareness$1.off("update", onAwarenessUpdate);
129
238
  window.removeEventListener("message", onMessage);
239
+ if (currentCleanup) {
240
+ currentCleanup();
241
+ }
130
242
  }
131
243
  };
132
244
  }
@@ -1 +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\nexport interface IframeBridgeProvider {\n serverClientId: number | null;\n dispose: () => 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\n const onYdocUpdate = (update: Uint8Array) => {\n if (applyingParentUpdate) return;\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 }\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 dispose: () => {\n ydoc.off(\"update\", onYdocUpdate);\n awareness.off(\"update\", onAwarenessUpdate);\n window.removeEventListener(\"message\", onMessage);\n },\n };\n}\n"],"names":["awareness","encodeAwarenessUpdate","Y","applyAwarenessUpdate"],"mappings":"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"}
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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"}
@@ -1,5 +1,13 @@
1
1
  import * as Y from "yjs";
2
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
+ }
3
11
  function readVarUint(data, pos) {
4
12
  let result = 0;
5
13
  let shift = 0;
@@ -52,8 +60,21 @@ function remapClientIdInUpdate(update, fromId, toId) {
52
60
  function createIframeBridgeProvider(ydoc, awareness) {
53
61
  let applyingParentUpdate = false;
54
62
  let serverClientId = null;
63
+ let currentCleanup = null;
64
+ let currentMxLike = null;
55
65
  const onYdocUpdate = (update) => {
56
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
+ }
57
78
  window.parent.postMessage(
58
79
  { type: "ydoc-update", payload: Array.from(update) },
59
80
  "*"
@@ -94,6 +115,22 @@ function createIframeBridgeProvider(ydoc, awareness) {
94
115
  applyingParentUpdate = true;
95
116
  applyAwarenessUpdate(awareness, remapped, null);
96
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;
97
134
  }
98
135
  };
99
136
  ydoc.on("update", onYdocUpdate);
@@ -104,10 +141,85 @@ function createIframeBridgeProvider(ydoc, awareness) {
104
141
  get serverClientId() {
105
142
  return serverClientId;
106
143
  },
107
- dispose: () => {
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: () => {
108
217
  ydoc.off("update", onYdocUpdate);
109
218
  awareness.off("update", onAwarenessUpdate);
110
219
  window.removeEventListener("message", onMessage);
220
+ if (currentCleanup) {
221
+ currentCleanup();
222
+ }
111
223
  }
112
224
  };
113
225
  }
@@ -1 +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\nexport interface IframeBridgeProvider {\n serverClientId: number | null;\n dispose: () => 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\n const onYdocUpdate = (update: Uint8Array) => {\n if (applyingParentUpdate) return;\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 }\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 dispose: () => {\n ydoc.off(\"update\", onYdocUpdate);\n awareness.off(\"update\", onAwarenessUpdate);\n window.removeEventListener(\"message\", onMessage);\n },\n };\n}\n"],"names":[],"mappings":"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"}
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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"}
@@ -19,10 +19,13 @@ function _interopNamespaceDefault(e) {
19
19
  return Object.freeze(n);
20
20
  }
21
21
  const Y__namespace = /* @__PURE__ */ _interopNamespaceDefault(Y);
22
- function createIframeBridgeServer(ydoc, awareness$1) {
22
+ const IFRAME_ORIGIN = {};
23
+ function createIframeBridgeServer(ydoc, awareness$1, options) {
24
+ const { undoManager } = options ?? {};
23
25
  const iframes = /* @__PURE__ */ new Map();
24
26
  const iframeReady = /* @__PURE__ */ new Set();
25
- const onYdocUpdate = (update) => {
27
+ const onYdocUpdate = (update, origin) => {
28
+ if (origin === IFRAME_ORIGIN) return;
26
29
  broadcastToAll("ydoc-update", update);
27
30
  };
28
31
  const onAwarenessUpdate = ({
@@ -35,9 +38,9 @@ function createIframeBridgeServer(ydoc, awareness$1) {
35
38
  const update = awareness.encodeAwarenessUpdate(awareness$1, changes);
36
39
  broadcastToAll("awareness-update", update);
37
40
  };
38
- function broadcastToAll(type, payload) {
41
+ function broadcastToAll(type, payload, excludeSource) {
39
42
  for (const iframe of iframes.values()) {
40
- if (iframe.contentWindow) {
43
+ if (iframe.contentWindow && iframe.contentWindow !== excludeSource) {
41
44
  iframe.contentWindow.postMessage({ type, payload }, "*");
42
45
  }
43
46
  }
@@ -80,9 +83,15 @@ function createIframeBridgeServer(ydoc, awareness$1) {
80
83
  "*"
81
84
  );
82
85
  } else if (msgType === "ydoc-update") {
83
- Y__namespace.applyUpdate(ydoc, new Uint8Array(payload));
86
+ const update = new Uint8Array(payload);
87
+ Y__namespace.applyUpdate(ydoc, update, IFRAME_ORIGIN);
88
+ broadcastToAll("ydoc-update", update, sourceWindow);
84
89
  } else if (msgType === "awareness-update") {
85
90
  awareness.applyAwarenessUpdate(awareness$1, new Uint8Array(payload), null);
91
+ } else if (msgType === "undo" && undoManager) {
92
+ undoManager.undo();
93
+ } else if (msgType === "redo" && undoManager) {
94
+ undoManager.redo();
86
95
  }
87
96
  };
88
97
  function addIframe(iframe, iframeId) {
@@ -92,16 +101,35 @@ function createIframeBridgeServer(ydoc, awareness$1) {
92
101
  iframes.delete(iframeId);
93
102
  iframeReady.delete(iframeId);
94
103
  }
104
+ const onUndoPopped = (e) => {
105
+ const t = e && (e.type || e.reason || e.kind);
106
+ if (t === "undo") {
107
+ broadcastToAll("undo", new Uint8Array());
108
+ } else if (t === "redo") {
109
+ broadcastToAll("redo", new Uint8Array());
110
+ }
111
+ };
112
+ const onStackCleared = () => {
113
+ broadcastToAll("clear", new Uint8Array());
114
+ };
95
115
  ydoc.on("update", onYdocUpdate);
96
116
  awareness$1.on("update", onAwarenessUpdate);
97
117
  window.addEventListener("message", onMessage);
118
+ if (undoManager) {
119
+ undoManager.on("stack-item-popped", onUndoPopped);
120
+ undoManager.on("stack-cleared", onStackCleared);
121
+ }
98
122
  return {
99
123
  addIframe,
100
124
  removeIframe,
101
- dispose: () => {
125
+ destroy: () => {
102
126
  ydoc.off("update", onYdocUpdate);
103
127
  awareness$1.off("update", onAwarenessUpdate);
104
128
  window.removeEventListener("message", onMessage);
129
+ if (undoManager) {
130
+ undoManager.off("stack-item-popped", onUndoPopped);
131
+ undoManager.off("stack-cleared", onStackCleared);
132
+ }
105
133
  iframes.clear();
106
134
  iframeReady.clear();
107
135
  }
@@ -1 +1 @@
1
- {"version":3,"file":"server.cjs.js","sources":["../../../iframe-bridge/src/server.ts"],"sourcesContent":["import * as Y from \"yjs\";\nimport {\n Awareness,\n applyAwarenessUpdate,\n encodeAwarenessUpdate,\n} from \"y-protocols/awareness\";\n\nexport interface IframeBridgeServer {\n addIframe: (iframe: HTMLIFrameElement, iframeId: string) => void;\n removeIframe: (iframeId: string) => void;\n dispose: () => void;\n}\n\nexport function createIframeBridgeServer(\n ydoc: Y.Doc,\n awareness: Awareness,\n): IframeBridgeServer {\n const iframes = new Map<string, HTMLIFrameElement>();\n const iframeReady = new Set<string>();\n\n const onYdocUpdate = (update: Uint8Array) => {\n broadcastToAll(\"ydoc-update\", update);\n };\n\n const onAwarenessUpdate = ({\n added,\n updated,\n removed,\n }: {\n added: number[];\n updated: number[];\n removed: number[];\n }) => {\n const changes = [...added, ...updated, ...removed];\n if (changes.length === 0) return;\n const update = encodeAwarenessUpdate(awareness, changes);\n broadcastToAll(\"awareness-update\", update);\n };\n\n function broadcastToAll(type: string, payload: Uint8Array) {\n for (const iframe of iframes.values()) {\n if (iframe.contentWindow) {\n iframe.contentWindow.postMessage({ type, payload }, \"*\");\n }\n }\n }\n\n const onMessage = (event: MessageEvent) => {\n let iframeId: string | null = null;\n for (const [id, iframe] of iframes) {\n if (event.source === iframe.contentWindow) {\n iframeId = id;\n break;\n }\n }\n if (!iframeId) return;\n\n const { type: msgType, payload } = event.data;\n const sourceWindow = event.source as Window;\n\n if (msgType === \"init\") {\n if (!iframeReady.has(iframeId)) {\n iframeReady.add(iframeId);\n }\n const docState = Y.encodeStateAsUpdate(ydoc);\n const awarenessState = encodeAwarenessUpdate(\n awareness,\n Array.from(awareness.getStates().keys()),\n );\n sourceWindow.postMessage(\n { type: \"ydoc-sync\", payload: Array.from(docState) },\n \"*\",\n );\n sourceWindow.postMessage(\n {\n type: \"awareness-sync\",\n payload: Array.from(awarenessState),\n serverClientId: awareness.clientID,\n },\n \"*\",\n );\n } else if (msgType === \"ping\") {\n sourceWindow.postMessage(\n { type: \"pong\", serverClientId: awareness.clientID },\n \"*\",\n );\n } else if (msgType === \"ydoc-update\") {\n Y.applyUpdate(ydoc, new Uint8Array(payload));\n } else if (msgType === \"awareness-update\") {\n applyAwarenessUpdate(awareness, new Uint8Array(payload), null);\n }\n };\n\n function addIframe(iframe: HTMLIFrameElement, iframeId: string) {\n iframes.set(iframeId, iframe);\n }\n\n function removeIframe(iframeId: string) {\n iframes.delete(iframeId);\n iframeReady.delete(iframeId);\n }\n\n ydoc.on(\"update\", onYdocUpdate);\n awareness.on(\"update\", onAwarenessUpdate);\n window.addEventListener(\"message\", onMessage);\n\n return {\n addIframe,\n removeIframe,\n dispose: () => {\n ydoc.off(\"update\", onYdocUpdate);\n awareness.off(\"update\", onAwarenessUpdate);\n window.removeEventListener(\"message\", onMessage);\n iframes.clear();\n iframeReady.clear();\n },\n };\n}\n"],"names":["awareness","encodeAwarenessUpdate","Y","applyAwarenessUpdate"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAaO,SAAS,yBACd,MACAA,aACoB;AACpB,QAAM,8BAAc,IAAA;AACpB,QAAM,kCAAkB,IAAA;AAExB,QAAM,eAAe,CAAC,WAAuB;AAC3C,mBAAe,eAAe,MAAM;AAAA,EACtC;AAEA,QAAM,oBAAoB,CAAC;AAAA,IACzB;AAAA,IACA;AAAA,IACA;AAAA,EAAA,MAKI;AACJ,UAAM,UAAU,CAAC,GAAG,OAAO,GAAG,SAAS,GAAG,OAAO;AACjD,QAAI,QAAQ,WAAW,EAAG;AAC1B,UAAM,SAASC,UAAAA,sBAAsBD,aAAW,OAAO;AACvD,mBAAe,oBAAoB,MAAM;AAAA,EAC3C;AAEA,WAAS,eAAe,MAAc,SAAqB;AACzD,eAAW,UAAU,QAAQ,UAAU;AACrC,UAAI,OAAO,eAAe;AACxB,eAAO,cAAc,YAAY,EAAE,MAAM,QAAA,GAAW,GAAG;AAAA,MACzD;AAAA,IACF;AAAA,EACF;AAEA,QAAM,YAAY,CAAC,UAAwB;AACzC,QAAI,WAA0B;AAC9B,eAAW,CAAC,IAAI,MAAM,KAAK,SAAS;AAClC,UAAI,MAAM,WAAW,OAAO,eAAe;AACzC,mBAAW;AACX;AAAA,MACF;AAAA,IACF;AACA,QAAI,CAAC,SAAU;AAEf,UAAM,EAAE,MAAM,SAAS,QAAA,IAAY,MAAM;AACzC,UAAM,eAAe,MAAM;AAE3B,QAAI,YAAY,QAAQ;AACtB,UAAI,CAAC,YAAY,IAAI,QAAQ,GAAG;AAC9B,oBAAY,IAAI,QAAQ;AAAA,MAC1B;AACA,YAAM,WAAWE,aAAE,oBAAoB,IAAI;AAC3C,YAAM,iBAAiBD,UAAAA;AAAAA,QACrBD;AAAAA,QACA,MAAM,KAAKA,YAAU,UAAA,EAAY,MAAM;AAAA,MAAA;AAEzC,mBAAa;AAAA,QACX,EAAE,MAAM,aAAa,SAAS,MAAM,KAAK,QAAQ,EAAA;AAAA,QACjD;AAAA,MAAA;AAEF,mBAAa;AAAA,QACX;AAAA,UACE,MAAM;AAAA,UACN,SAAS,MAAM,KAAK,cAAc;AAAA,UAClC,gBAAgBA,YAAU;AAAA,QAAA;AAAA,QAE5B;AAAA,MAAA;AAAA,IAEJ,WAAW,YAAY,QAAQ;AAC7B,mBAAa;AAAA,QACX,EAAE,MAAM,QAAQ,gBAAgBA,YAAU,SAAA;AAAA,QAC1C;AAAA,MAAA;AAAA,IAEJ,WAAW,YAAY,eAAe;AACpCE,mBAAE,YAAY,MAAM,IAAI,WAAW,OAAO,CAAC;AAAA,IAC7C,WAAW,YAAY,oBAAoB;AACzCC,gBAAAA,qBAAqBH,aAAW,IAAI,WAAW,OAAO,GAAG,IAAI;AAAA,IAC/D;AAAA,EACF;AAEA,WAAS,UAAU,QAA2B,UAAkB;AAC9D,YAAQ,IAAI,UAAU,MAAM;AAAA,EAC9B;AAEA,WAAS,aAAa,UAAkB;AACtC,YAAQ,OAAO,QAAQ;AACvB,gBAAY,OAAO,QAAQ;AAAA,EAC7B;AAEA,OAAK,GAAG,UAAU,YAAY;AAC9BA,cAAU,GAAG,UAAU,iBAAiB;AACxC,SAAO,iBAAiB,WAAW,SAAS;AAE5C,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA,SAAS,MAAM;AACb,WAAK,IAAI,UAAU,YAAY;AAC/BA,kBAAU,IAAI,UAAU,iBAAiB;AACzC,aAAO,oBAAoB,WAAW,SAAS;AAC/C,cAAQ,MAAA;AACR,kBAAY,MAAA;AAAA,IACd;AAAA,EAAA;AAEJ;;"}
1
+ {"version":3,"file":"server.cjs.js","sources":["../../../iframe-bridge/src/origin.ts","../../../iframe-bridge/src/server.ts"],"sourcesContent":["/**\n * iframe bridge 内部变更的 origin 标识。\n * 当 provider 端产生 ydoc-update 时,使用此 origin 标记,\n * 以便 server 端的 UndoManager 能正确追踪来自 iframe 的变更。\n */\nexport const IFRAME_ORIGIN: object = {};\n","import * as Y from \"yjs\";\nimport {\n Awareness,\n applyAwarenessUpdate,\n encodeAwarenessUpdate,\n} from \"y-protocols/awareness\";\nimport { IFRAME_ORIGIN } from \"./origin.js\";\n\nexport interface IframeBridgeServerOptions {\n undoManager?: Y.UndoManager;\n}\n\nexport interface IframeBridgeServer {\n addIframe: (iframe: HTMLIFrameElement, iframeId: string) => void;\n removeIframe: (iframeId: string) => void;\n destroy: () => void;\n}\n\nexport function createIframeBridgeServer(\n ydoc: Y.Doc,\n awareness: Awareness,\n options?: IframeBridgeServerOptions,\n): IframeBridgeServer {\n const { undoManager } = options ?? {};\n const iframes = new Map<string, HTMLIFrameElement>();\n const iframeReady = new Set<string>();\n\n const onYdocUpdate = (update: Uint8Array, origin: unknown) => {\n if (origin === IFRAME_ORIGIN) return;\n broadcastToAll(\"ydoc-update\", update);\n };\n\n const onAwarenessUpdate = ({\n added,\n updated,\n removed,\n }: {\n added: number[];\n updated: number[];\n removed: number[];\n }) => {\n const changes = [...added, ...updated, ...removed];\n if (changes.length === 0) return;\n const update = encodeAwarenessUpdate(awareness, changes);\n broadcastToAll(\"awareness-update\", update);\n };\n\n function broadcastToAll(type: string, payload: Uint8Array, excludeSource?: Window) {\n for (const iframe of iframes.values()) {\n if (iframe.contentWindow && iframe.contentWindow !== excludeSource) {\n iframe.contentWindow.postMessage({ type, payload }, \"*\");\n }\n }\n }\n\n const onMessage = (event: MessageEvent) => {\n let iframeId: string | null = null;\n for (const [id, iframe] of iframes) {\n if (event.source === iframe.contentWindow) {\n iframeId = id;\n break;\n }\n }\n if (!iframeId) return;\n\n const { type: msgType, payload } = event.data;\n const sourceWindow = event.source as Window;\n\n if (msgType === \"init\") {\n if (!iframeReady.has(iframeId)) {\n iframeReady.add(iframeId);\n }\n const docState = Y.encodeStateAsUpdate(ydoc);\n const awarenessState = encodeAwarenessUpdate(\n awareness,\n Array.from(awareness.getStates().keys()),\n );\n sourceWindow.postMessage(\n { type: \"ydoc-sync\", payload: Array.from(docState) },\n \"*\",\n );\n sourceWindow.postMessage(\n {\n type: \"awareness-sync\",\n payload: Array.from(awarenessState),\n serverClientId: awareness.clientID,\n },\n \"*\",\n );\n } else if (msgType === \"ping\") {\n sourceWindow.postMessage(\n { type: \"pong\", serverClientId: awareness.clientID },\n \"*\",\n );\n } else if (msgType === \"ydoc-update\") {\n const update = new Uint8Array(payload);\n Y.applyUpdate(ydoc, update, IFRAME_ORIGIN);\n broadcastToAll(\"ydoc-update\", update, sourceWindow);\n } else if (msgType === \"awareness-update\") {\n applyAwarenessUpdate(awareness, new Uint8Array(payload), null);\n } else if (msgType === \"undo\" && undoManager) {\n undoManager.undo();\n } else if (msgType === \"redo\" && undoManager) {\n undoManager.redo();\n }\n };\n\n function addIframe(iframe: HTMLIFrameElement, iframeId: string) {\n iframes.set(iframeId, iframe);\n }\n\n function removeIframe(iframeId: string) {\n iframes.delete(iframeId);\n iframeReady.delete(iframeId);\n }\n\n const onUndoPopped = (e: { type?: string; reason?: string; kind?: string }) => {\n const t = e && (e.type || e.reason || e.kind);\n if (t === \"undo\") {\n broadcastToAll(\"undo\", new Uint8Array());\n } else if (t === \"redo\") {\n broadcastToAll(\"redo\", new Uint8Array());\n }\n };\n\n const onStackCleared = () => {\n broadcastToAll(\"clear\", new Uint8Array());\n };\n\n ydoc.on(\"update\", onYdocUpdate);\n awareness.on(\"update\", onAwarenessUpdate);\n window.addEventListener(\"message\", onMessage);\n if (undoManager) {\n undoManager.on(\"stack-item-popped\", onUndoPopped);\n undoManager.on(\"stack-cleared\", onStackCleared);\n }\n\n return {\n addIframe,\n removeIframe,\n destroy: () => {\n ydoc.off(\"update\", onYdocUpdate);\n awareness.off(\"update\", onAwarenessUpdate);\n window.removeEventListener(\"message\", onMessage);\n if (undoManager) {\n undoManager.off(\"stack-item-popped\", onUndoPopped);\n undoManager.off(\"stack-cleared\", onStackCleared);\n }\n iframes.clear();\n iframeReady.clear();\n },\n };\n}\n"],"names":["awareness","encodeAwarenessUpdate","Y","applyAwarenessUpdate"],"mappings":"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"}
@@ -1,9 +1,12 @@
1
1
  import * as Y from "yjs";
2
2
  import { encodeAwarenessUpdate, applyAwarenessUpdate } from "y-protocols/awareness";
3
- function createIframeBridgeServer(ydoc, awareness) {
3
+ const IFRAME_ORIGIN = {};
4
+ function createIframeBridgeServer(ydoc, awareness, options) {
5
+ const { undoManager } = options ?? {};
4
6
  const iframes = /* @__PURE__ */ new Map();
5
7
  const iframeReady = /* @__PURE__ */ new Set();
6
- const onYdocUpdate = (update) => {
8
+ const onYdocUpdate = (update, origin) => {
9
+ if (origin === IFRAME_ORIGIN) return;
7
10
  broadcastToAll("ydoc-update", update);
8
11
  };
9
12
  const onAwarenessUpdate = ({
@@ -16,9 +19,9 @@ function createIframeBridgeServer(ydoc, awareness) {
16
19
  const update = encodeAwarenessUpdate(awareness, changes);
17
20
  broadcastToAll("awareness-update", update);
18
21
  };
19
- function broadcastToAll(type, payload) {
22
+ function broadcastToAll(type, payload, excludeSource) {
20
23
  for (const iframe of iframes.values()) {
21
- if (iframe.contentWindow) {
24
+ if (iframe.contentWindow && iframe.contentWindow !== excludeSource) {
22
25
  iframe.contentWindow.postMessage({ type, payload }, "*");
23
26
  }
24
27
  }
@@ -61,9 +64,15 @@ function createIframeBridgeServer(ydoc, awareness) {
61
64
  "*"
62
65
  );
63
66
  } else if (msgType === "ydoc-update") {
64
- Y.applyUpdate(ydoc, new Uint8Array(payload));
67
+ const update = new Uint8Array(payload);
68
+ Y.applyUpdate(ydoc, update, IFRAME_ORIGIN);
69
+ broadcastToAll("ydoc-update", update, sourceWindow);
65
70
  } else if (msgType === "awareness-update") {
66
71
  applyAwarenessUpdate(awareness, new Uint8Array(payload), null);
72
+ } else if (msgType === "undo" && undoManager) {
73
+ undoManager.undo();
74
+ } else if (msgType === "redo" && undoManager) {
75
+ undoManager.redo();
67
76
  }
68
77
  };
69
78
  function addIframe(iframe, iframeId) {
@@ -73,16 +82,35 @@ function createIframeBridgeServer(ydoc, awareness) {
73
82
  iframes.delete(iframeId);
74
83
  iframeReady.delete(iframeId);
75
84
  }
85
+ const onUndoPopped = (e) => {
86
+ const t = e && (e.type || e.reason || e.kind);
87
+ if (t === "undo") {
88
+ broadcastToAll("undo", new Uint8Array());
89
+ } else if (t === "redo") {
90
+ broadcastToAll("redo", new Uint8Array());
91
+ }
92
+ };
93
+ const onStackCleared = () => {
94
+ broadcastToAll("clear", new Uint8Array());
95
+ };
76
96
  ydoc.on("update", onYdocUpdate);
77
97
  awareness.on("update", onAwarenessUpdate);
78
98
  window.addEventListener("message", onMessage);
99
+ if (undoManager) {
100
+ undoManager.on("stack-item-popped", onUndoPopped);
101
+ undoManager.on("stack-cleared", onStackCleared);
102
+ }
79
103
  return {
80
104
  addIframe,
81
105
  removeIframe,
82
- dispose: () => {
106
+ destroy: () => {
83
107
  ydoc.off("update", onYdocUpdate);
84
108
  awareness.off("update", onAwarenessUpdate);
85
109
  window.removeEventListener("message", onMessage);
110
+ if (undoManager) {
111
+ undoManager.off("stack-item-popped", onUndoPopped);
112
+ undoManager.off("stack-cleared", onStackCleared);
113
+ }
86
114
  iframes.clear();
87
115
  iframeReady.clear();
88
116
  }