y-mxgraph 0.6.0 → 0.6.2
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/helper/yjs.d.ts +10 -0
- package/helper/yjs.d.ts.map +1 -0
- package/iframe-bridge/provider.cjs +70 -4
- package/iframe-bridge/provider.cjs.map +1 -1
- package/iframe-bridge/provider.js +70 -4
- package/iframe-bridge/provider.js.map +1 -1
- package/iframe-bridge/server.cjs +54 -2
- package/iframe-bridge/server.cjs.map +1 -1
- package/iframe-bridge/server.js +54 -2
- package/iframe-bridge/server.js.map +1 -1
- package/{index-DBU79c0g.js → index-D8PKHH-R.js} +20 -16
- package/index-D8PKHH-R.js.map +1 -0
- package/{index-CcOHH2TY.cjs → index-DkYwhpfJ.cjs} +20 -16
- package/index-DkYwhpfJ.cjs.map +1 -0
- package/models/diagram.d.ts.map +1 -1
- package/models/mxGraphModel.d.ts.map +1 -1
- package/models/mxfile.d.ts.map +1 -1
- package/package.json +1 -1
- package/transform/index.d.ts.map +1 -1
- package/transform.cjs +1 -1
- package/transform.js +1 -1
- package/y-mxgraph.cjs +1 -1
- package/y-mxgraph.js +1 -1
- package/index-CcOHH2TY.cjs.map +0 -1
- package/index-DBU79c0g.js.map +0 -1
package/helper/yjs.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import * as Y from "yjs";
|
|
2
|
+
/**
|
|
3
|
+
* 从 Y.Map 中安全获取子 Map(格式固定,转型安全)
|
|
4
|
+
*/
|
|
5
|
+
export declare function getMap<T = unknown>(parent: Y.Map<unknown>, key: string): Y.Map<T> | undefined;
|
|
6
|
+
/**
|
|
7
|
+
* 从 Y.Map 中安全获取子 Array(格式固定,转型安全)
|
|
8
|
+
*/
|
|
9
|
+
export declare function getArray<T = unknown>(parent: Y.Map<unknown>, key: string): Y.Array<T> | undefined;
|
|
10
|
+
//# sourceMappingURL=yjs.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"yjs.d.ts","sourceRoot":"","sources":["../../src/helper/yjs.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,KAAK,CAAC;AAEzB;;GAEG;AACH,wBAAgB,MAAM,CAAC,CAAC,GAAG,OAAO,EAChC,MAAM,EAAE,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,EACtB,GAAG,EAAE,MAAM,GACV,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,SAAS,CAEtB;AAED;;GAEG;AACH,wBAAgB,QAAQ,CAAC,CAAC,GAAG,OAAO,EAClC,MAAM,EAAE,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,EACtB,GAAG,EAAE,MAAM,GACV,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,SAAS,CAExB"}
|
|
@@ -81,6 +81,30 @@ function createIframeBridgeProvider(ydoc, awareness$1) {
|
|
|
81
81
|
let serverClientId = null;
|
|
82
82
|
let currentCleanup = null;
|
|
83
83
|
let currentMxLike = null;
|
|
84
|
+
let connected = false;
|
|
85
|
+
let initRetryTimer = null;
|
|
86
|
+
const connectListeners = /* @__PURE__ */ new Set();
|
|
87
|
+
const disconnectListeners = /* @__PURE__ */ new Set();
|
|
88
|
+
function setConnected(value) {
|
|
89
|
+
if (connected === value) return;
|
|
90
|
+
connected = value;
|
|
91
|
+
if (value) {
|
|
92
|
+
connectListeners.forEach((fn) => fn());
|
|
93
|
+
} else {
|
|
94
|
+
disconnectListeners.forEach((fn) => fn());
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
function startInitRetry() {
|
|
98
|
+
if (initRetryTimer) {
|
|
99
|
+
clearInterval(initRetryTimer);
|
|
100
|
+
}
|
|
101
|
+
window.parent.postMessage({ type: "init" }, "*");
|
|
102
|
+
initRetryTimer = setInterval(() => {
|
|
103
|
+
if (!connected) {
|
|
104
|
+
window.parent.postMessage({ type: "init" }, "*");
|
|
105
|
+
}
|
|
106
|
+
}, 1e3);
|
|
107
|
+
}
|
|
84
108
|
const onYdocUpdate = (update, origin) => {
|
|
85
109
|
if (applyingParentUpdate) return;
|
|
86
110
|
const isBaseline = origin === null || origin === void 0;
|
|
@@ -118,6 +142,13 @@ function createIframeBridgeProvider(ydoc, awareness$1) {
|
|
|
118
142
|
applyingParentUpdate = true;
|
|
119
143
|
Y__namespace.applyUpdate(ydoc, new Uint8Array(payload));
|
|
120
144
|
applyingParentUpdate = false;
|
|
145
|
+
if (type === "ydoc-sync" && !connected) {
|
|
146
|
+
setConnected(true);
|
|
147
|
+
if (initRetryTimer) {
|
|
148
|
+
clearInterval(initRetryTimer);
|
|
149
|
+
initRetryTimer = null;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
121
152
|
} else if (type === "awareness-sync" || type === "awareness-update") {
|
|
122
153
|
if (receivedServerId != null) {
|
|
123
154
|
serverClientId = receivedServerId;
|
|
@@ -136,19 +167,28 @@ function createIframeBridgeProvider(ydoc, awareness$1) {
|
|
|
136
167
|
if (newTotal === 0) {
|
|
137
168
|
currentMxLike.fireEvent(createMxEventObject("clear"));
|
|
138
169
|
} else if (newIndex < oldIndex) {
|
|
139
|
-
currentMxLike.fireEvent(
|
|
170
|
+
currentMxLike.fireEvent(
|
|
171
|
+
createMxEventObject("undo", { edit: { changes: [] } })
|
|
172
|
+
);
|
|
140
173
|
} else if (newIndex > oldIndex) {
|
|
141
|
-
currentMxLike.fireEvent(
|
|
174
|
+
currentMxLike.fireEvent(
|
|
175
|
+
createMxEventObject("redo", { edit: { changes: [] } })
|
|
176
|
+
);
|
|
142
177
|
} else {
|
|
143
|
-
currentMxLike.fireEvent(
|
|
178
|
+
currentMxLike.fireEvent(
|
|
179
|
+
createMxEventObject("add", { edit: { changes: [] } })
|
|
180
|
+
);
|
|
144
181
|
}
|
|
145
182
|
applyingParentUpdate = false;
|
|
183
|
+
} else if (type === "disconnect") {
|
|
184
|
+
setConnected(false);
|
|
185
|
+
startInitRetry();
|
|
146
186
|
}
|
|
147
187
|
};
|
|
148
188
|
ydoc.on("update", onYdocUpdate);
|
|
149
189
|
awareness$1.on("update", onAwarenessUpdate);
|
|
150
190
|
window.addEventListener("message", onMessage);
|
|
151
|
-
|
|
191
|
+
startInitRetry();
|
|
152
192
|
setTimeout(() => {
|
|
153
193
|
window.parent.postMessage({ type: "ping" }, "*");
|
|
154
194
|
}, 100);
|
|
@@ -156,6 +196,26 @@ function createIframeBridgeProvider(ydoc, awareness$1) {
|
|
|
156
196
|
get serverClientId() {
|
|
157
197
|
return serverClientId;
|
|
158
198
|
},
|
|
199
|
+
get connected() {
|
|
200
|
+
return connected;
|
|
201
|
+
},
|
|
202
|
+
onConnect(fn) {
|
|
203
|
+
connectListeners.add(fn);
|
|
204
|
+
return () => connectListeners.delete(fn);
|
|
205
|
+
},
|
|
206
|
+
onDisconnect(fn) {
|
|
207
|
+
disconnectListeners.add(fn);
|
|
208
|
+
return () => disconnectListeners.delete(fn);
|
|
209
|
+
},
|
|
210
|
+
on(event, fn) {
|
|
211
|
+
if (event === "connect") {
|
|
212
|
+
connectListeners.add(fn);
|
|
213
|
+
return () => connectListeners.delete(fn);
|
|
214
|
+
} else {
|
|
215
|
+
disconnectListeners.add(fn);
|
|
216
|
+
return () => disconnectListeners.delete(fn);
|
|
217
|
+
}
|
|
218
|
+
},
|
|
159
219
|
takeoverUndoManager(file) {
|
|
160
220
|
if (currentCleanup) {
|
|
161
221
|
currentCleanup();
|
|
@@ -235,6 +295,12 @@ function createIframeBridgeProvider(ydoc, awareness$1) {
|
|
|
235
295
|
ydoc.off("update", onYdocUpdate);
|
|
236
296
|
awareness$1.off("update", onAwarenessUpdate);
|
|
237
297
|
window.removeEventListener("message", onMessage);
|
|
298
|
+
if (initRetryTimer) {
|
|
299
|
+
clearInterval(initRetryTimer);
|
|
300
|
+
initRetryTimer = null;
|
|
301
|
+
}
|
|
302
|
+
connectListeners.clear();
|
|
303
|
+
disconnectListeners.clear();
|
|
238
304
|
if (currentCleanup) {
|
|
239
305
|
currentCleanup();
|
|
240
306
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"provider.cjs","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, origin: unknown) => {\n if (applyingParentUpdate) return;\n // 检测基线数据:origin 为 null 时是 xml2ydoc 首次初始化\n const isBaseline = origin === null || origin === undefined;\n window.parent.postMessage(\n { type: \"ydoc-update\", payload: Array.from(update), isBaseline },\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 // 只发送本地 clientID 的更新给父页面\n // 其他 peers 的更新通过父页面的 WebrtcProvider 同步,不应该从 iframe 回传\n const localClientId = awareness.clientID;\n const localChanged = changes.includes(localClientId);\n if (!localChanged) return;\n\n const update = encodeAwarenessUpdate(awareness, [localClientId]);\n const remapped =\n serverClientId != null\n ? remapClientIdInUpdate(update, localClientId, 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 // 直接使用 server 发送的原始 clientID,不做映射\n // iframe 的 awareness 中会包含:\n // - iframe 自身的 clientID (awareness.clientID)\n // - server 的 clientID (serverClientId)\n // - 其他 Webrtc peers 的 clientID\n applyingParentUpdate = true;\n applyAwarenessUpdate(awareness, new Uint8Array(payload), null);\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(createMxEventObject(\"undo\", { edit: { changes: [] } }));\n } else if (newIndex > oldIndex) {\n currentMxLike.fireEvent(createMxEventObject(\"redo\", { edit: { changes: [] } }));\n } else {\n currentMxLike.fireEvent(createMxEventObject(\"add\", { edit: { changes: [] } }));\n }\n\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 // 发送 ping 获取 serverClientId\n setTimeout(() => {\n window.parent.postMessage({ type: \"ping\" }, \"*\");\n }, 100);\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 \"\");\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 (currentCleanup) {\n currentCleanup();\n }\n },\n };\n}\n"],"names":["awareness","encodeAwarenessUpdate","Y","applyAwarenessUpdate"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"provider.cjs","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 connected: boolean;\n onConnect: (fn: () => void) => () => void;\n onDisconnect: (fn: () => void) => () => void;\n on: (event: \"connect\" | \"disconnect\", fn: () => void) => () => 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 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 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 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 window.parent.postMessage({ type: \"init\" }, \"*\");\n initRetryTimer = setInterval(() => {\n if (!connected) {\n window.parent.postMessage({ 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 window.parent.postMessage(\n { type: \"ydoc-update\", payload: Array.from(update), isBaseline },\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 // 只发送本地 clientID 的更新给父页面\n // 其他 peers 的更新通过父页面的 WebrtcProvider 同步,不应该从 iframe 回传\n const localClientId = awareness.clientID;\n const localChanged = changes.includes(localClientId);\n if (!localChanged) return;\n\n const update = encodeAwarenessUpdate(awareness, [localClientId]);\n const remapped =\n serverClientId != null\n ? remapClientIdInUpdate(update, localClientId, 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 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 if (receivedServerId != null) {\n serverClientId = receivedServerId;\n }\n\n // 直接使用 server 发送的原始 clientID,不做映射\n // iframe 的 awareness 中会包含:\n // - iframe 自身的 clientID (awareness.clientID)\n // - server 的 clientID (serverClientId)\n // - 其他 Webrtc peers 的 clientID\n applyingParentUpdate = true;\n applyAwarenessUpdate(awareness, new Uint8Array(payload), null);\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 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":["awareness","encodeAwarenessUpdate","Y","applyAwarenessUpdate"],"mappings":";;;;;;;;;;;;;;;;;;;;;AASA,SAAS,oBAAoB,MAAc,OAAiC;AAC1E,QAAM,SAAS,SAAS,CAAA;AACxB,SAAO;AAAA,IACL;AAAA,IACA,SAAS,MAAM;AAAA,IACf,aAAa,CAAC,MAAc,OAAO,CAAC;AAAA,EAAA;AAExC;AAqCA,SAAS,YAAY,MAAkB,KAA+B;AACpE,MAAI,SAAS;AACb,MAAI,QAAQ;AACZ,MAAI;AACJ,KAAG;AACD,WAAO,KAAK,KAAK;AACjB,eAAW,OAAO,QAAS;AAC3B,aAAS;AAAA,EACX,SAAS,QAAQ;AACjB,SAAO,CAAC,WAAW,GAAG,GAAG;AAC3B;AAEA,SAAS,aAAa,OAAyB;AAC7C,QAAM,QAAkB,CAAA;AACxB,SAAO,QAAQ,KAAM;AACnB,UAAM,KAAM,QAAQ,MAAQ,GAAI;AAChC,eAAW;AAAA,EACb;AACA,QAAM,KAAK,KAAK;AAChB,SAAO;AACT;AAEA,SAAS,cAAc,MAAkB,KAA+B;AACtE,QAAM,CAAC,KAAK,IAAI,IAAI,YAAY,MAAM,GAAG;AACzC,QAAM,MAAM,IAAI,YAAA,EAAc,OAAO,KAAK,SAAS,MAAM,OAAO,GAAG,CAAC;AACpE,SAAO,CAAC,KAAK,OAAO,GAAG;AACzB;AAEA,SAAS,eAAe,KAAuB;AAC7C,QAAM,UAAU,IAAI,cAAc,OAAO,GAAG;AAC5C,SAAO,CAAC,GAAG,aAAa,QAAQ,MAAM,GAAG,GAAG,OAAO;AACrD;AAEA,SAAS,sBACP,QACA,QACA,MACY;AACZ,QAAM,SAAmB,CAAA;AACzB,MAAI,MAAM;AAEV,QAAM,CAAC,OAAO,IAAI,IAAI,YAAY,QAAQ,GAAG;AAC7C,QAAM;AACN,SAAO,KAAK,GAAG,aAAa,KAAK,CAAC;AAElC,WAAS,IAAI,GAAG,IAAI,OAAO,KAAK;AAC9B,UAAM,CAAC,UAAU,IAAI,IAAI,YAAY,QAAQ,GAAG;AAChD,UAAM;AACN,UAAM,CAAC,OAAO,IAAI,IAAI,YAAY,QAAQ,GAAG;AAC7C,UAAM;AACN,UAAM,CAAC,OAAO,IAAI,IAAI,cAAc,QAAQ,GAAG;AAC/C,UAAM;AAEN,UAAM,WAAW,aAAa,SAAS,OAAO;AAC9C,WAAO,KAAK,GAAG,aAAa,QAAQ,CAAC;AACrC,WAAO,KAAK,GAAG,aAAa,KAAK,CAAC;AAClC,WAAO,KAAK,GAAG,eAAe,KAAK,CAAC;AAAA,EACtC;AAEA,SAAO,IAAI,WAAW,MAAM;AAC9B;AAEO,SAAS,2BACd,MACAA,aACsB;AACtB,MAAI,uBAAuB;AAC3B,MAAI,iBAAgC;AACpC,MAAI,iBAAsC;AAC1C,MAAI,gBAA+B;AACnC,MAAI,YAAY;AAChB,MAAI,iBAAwD;AAC5D,QAAM,uCAAuB,IAAA;AAC7B,QAAM,0CAA0B,IAAA;AAEhC,WAAS,aAAa,OAAgB;AACpC,QAAI,cAAc,MAAO;AACzB,gBAAY;AACZ,QAAI,OAAO;AACT,uBAAiB,QAAQ,CAAC,OAAO,GAAA,CAAI;AAAA,IACvC,OAAO;AACL,0BAAoB,QAAQ,CAAC,OAAO,GAAA,CAAI;AAAA,IAC1C;AAAA,EACF;AAEA,WAAS,iBAAiB;AACxB,QAAI,gBAAgB;AAClB,oBAAc,cAAc;AAAA,IAC9B;AACA,WAAO,OAAO,YAAY,EAAE,MAAM,OAAA,GAAU,GAAG;AAC/C,qBAAiB,YAAY,MAAM;AACjC,UAAI,CAAC,WAAW;AACd,eAAO,OAAO,YAAY,EAAE,MAAM,OAAA,GAAU,GAAG;AAAA,MACjD;AAAA,IACF,GAAG,GAAI;AAAA,EACT;AAEA,QAAM,eAAe,CAAC,QAAoB,WAAoB;AAC5D,QAAI,qBAAsB;AAE1B,UAAM,aAAa,WAAW,QAAQ,WAAW;AACjD,WAAO,OAAO;AAAA,MACZ,EAAE,MAAM,eAAe,SAAS,MAAM,KAAK,MAAM,GAAG,WAAA;AAAA,MACpD;AAAA,IAAA;AAAA,EAEJ;AAEA,QAAM,oBAAoB,CAAC;AAAA,IACzB;AAAA,IACA;AAAA,IACA;AAAA,EAAA,MAKI;AACJ,QAAI,qBAAsB;AAC1B,UAAM,UAAU,CAAC,GAAG,OAAO,GAAG,SAAS,GAAG,OAAO;AACjD,QAAI,QAAQ,WAAW,EAAG;AAI1B,UAAM,gBAAgBA,YAAU;AAChC,UAAM,eAAe,QAAQ,SAAS,aAAa;AACnD,QAAI,CAAC,aAAc;AAEnB,UAAM,SAASC,UAAAA,sBAAsBD,aAAW,CAAC,aAAa,CAAC;AAC/D,UAAM,WACJ,kBAAkB,OACd,sBAAsB,QAAQ,eAAe,cAAc,IAC3D;AAEN,WAAO,OAAO;AAAA,MACZ,EAAE,MAAM,oBAAoB,SAAS,MAAM,KAAK,QAAQ,EAAA;AAAA,MACxD;AAAA,IAAA;AAAA,EAEJ;AAEA,QAAM,YAAY,CAAC,UAAwB;AACzC,QAAI,MAAM,WAAW,OAAO,OAAQ;AACpC,UAAM,EAAE,MAAM,SAAS,gBAAgB,iBAAA,IAAqB,MAAM;AAElE,QAAI,SAAS,UAAU,oBAAoB,MAAM;AAC/C,uBAAiB;AACjB;AAAA,IACF;AAEA,QAAI,SAAS,eAAe,SAAS,eAAe;AAClD,6BAAuB;AACvBE,mBAAE,YAAY,MAAM,IAAI,WAAW,OAAO,CAAC;AAC3C,6BAAuB;AACvB,UAAI,SAAS,eAAe,CAAC,WAAW;AACtC,qBAAa,IAAI;AACjB,YAAI,gBAAgB;AAClB,wBAAc,cAAc;AAC5B,2BAAiB;AAAA,QACnB;AAAA,MACF;AAAA,IACF,WAAW,SAAS,oBAAoB,SAAS,oBAAoB;AACnE,UAAI,oBAAoB,MAAM;AAC5B,yBAAiB;AAAA,MACnB;AAOA,6BAAuB;AACvBC,gBAAAA,qBAAqBH,aAAW,IAAI,WAAW,OAAO,GAAG,IAAI;AAC7D,6BAAuB;AAAA,IACzB,WAAW,SAAS,gBAAgB,eAAe;AAEjD,YAAM,EAAE,eAAe,cAAA,IAAkB,MAAM;AAE/C,YAAM,WAAW,cAAc;AAC/B,YAAM,WAAW,iBAAiB;AAClC,YAAM,YAAY,iBAAiB,MAAM,iBAAiB;AAG1D,6BAAuB;AAGvB,oBAAc,UAAU,IAAI,MAAM,QAAQ,EAAE,KAAK,EAAE;AACnD,oBAAc,iBAAiB;AAG/B,UAAI,aAAa,GAAG;AAClB,sBAAc,UAAU,oBAAoB,OAAO,CAAC;AAAA,MACtD,WAAW,WAAW,UAAU;AAC9B,sBAAc;AAAA,UACZ,oBAAoB,QAAQ,EAAE,MAAM,EAAE,SAAS,CAAA,EAAC,EAAE,CAAG;AAAA,QAAA;AAAA,MAEzD,WAAW,WAAW,UAAU;AAC9B,sBAAc;AAAA,UACZ,oBAAoB,QAAQ,EAAE,MAAM,EAAE,SAAS,CAAA,EAAC,EAAE,CAAG;AAAA,QAAA;AAAA,MAEzD,OAAO;AACL,sBAAc;AAAA,UACZ,oBAAoB,OAAO,EAAE,MAAM,EAAE,SAAS,CAAA,EAAC,EAAE,CAAG;AAAA,QAAA;AAAA,MAExD;AAEA,6BAAuB;AAAA,IACzB,WAAW,SAAS,cAAc;AAChC,mBAAa,KAAK;AAClB,qBAAA;AAAA,IACF;AAAA,EACF;AAEA,OAAK,GAAG,UAAU,YAAY;AAC9BA,cAAU,GAAG,UAAU,iBAAiB;AACxC,SAAO,iBAAiB,WAAW,SAAS;AAE5C,iBAAA;AAGA,aAAW,MAAM;AACf,WAAO,OAAO,YAAY,EAAE,MAAM,OAAA,GAAU,GAAG;AAAA,EACjD,GAAG,GAAG;AAEN,SAAO;AAAA,IACL,IAAI,iBAAiB;AACnB,aAAO;AAAA,IACT;AAAA,IACA,IAAI,YAAY;AACd,aAAO;AAAA,IACT;AAAA,IACA,UAAU,IAAgB;AACxB,uBAAiB,IAAI,EAAE;AACvB,aAAO,MAAM,iBAAiB,OAAO,EAAE;AAAA,IACzC;AAAA,IACA,aAAa,IAAgB;AAC3B,0BAAoB,IAAI,EAAE;AAC1B,aAAO,MAAM,oBAAoB,OAAO,EAAE;AAAA,IAC5C;AAAA,IACA,GAAG,OAAiC,IAAgB;AAClD,UAAI,UAAU,WAAW;AACvB,yBAAiB,IAAI,EAAE;AACvB,eAAO,MAAM,iBAAiB,OAAO,EAAE;AAAA,MACzC,OAAO;AACL,4BAAoB,IAAI,EAAE;AAC1B,eAAO,MAAM,oBAAoB,OAAO,EAAE;AAAA,MAC5C;AAAA,IACF;AAAA,IACA,oBAAoB,MAAkB;AACpC,UAAI,gBAAgB;AAClB,uBAAA;AAAA,MACF;AAEA,YAAM,SAAS,KAAK,MAAA,EAAQ;AAC5B,YAAM,oBAAoB,OAAO;AAEjC,YAAM,QAAqC,CAAA;AAC3C,YAAM,MAAM,MAAM,QAAQ,qBAAA,OAAA,SAAA,kBAAmB,cAAc,IACtD,kBAAkB,iBACnB,CAAA;AACJ,eAAS,IAAI,GAAG,IAAI,IAAI,IAAI,QAAQ,KAAK,GAAG;AAC1C,cAAM,MAAM,OAAO,IAAI,CAAC,CAAC;AACzB,cAAM,KAAK,IAAI,IAAI,CAAC;AACpB,cAAM,KAAK,CAAC,KAAK,EAAE,CAAC;AAAA,MACtB;AAEA,YAAM,SAAiB;AAAA,QACrB,gBAAgB,CAAA;AAAA,QAChB,SAAS,CAAA;AAAA,QACT,gBAAgB;AAAA,QAEhB,YAAY,MAAc,IAAgB;AACxC,eAAK,eAAe,KAAK,MAAM,EAAE;AAAA,QACnC;AAAA,QAEA,UAAU,KAAc;AArUhC,cAAA,IAAA;AAsUU,gBAAM,aACH,OAAA,OAAA,SAAA,IAAuC,WACtC,MAAA,KAAA,OAAA,OAAA,SAAA,IAAgD,YAAhD,OAAA,SAAA,GAAA,KAAA,GAAA,MAAA,OAAA,KACA;AACJ,mBAAS,IAAI,GAAG,IAAI,IAAI,KAAK,eAAe,QAAQ,KAAK,GAAG;AAC1D,kBAAM,MAAM,KAAK,eAAe,CAAC;AACjC,kBAAM,WAAW,KAAK,eAAe,IAAI,CAAC;AAC1C,gBAAI,QAAQ,WAAW;AACrB,kBAAI;AACF,yBAAS,MAAM,GAAG;AAAA,cACpB,SAAS,GAAG;AACV,wBAAQ;AAAA,kBACN;AAAA,kBACA;AAAA,gBAAA;AAAA,cAEJ;AAAA,YACF;AAAA,UACF;AAAA,QACF;AAAA,QAEA,UAAmB;AACjB,iBAAO,KAAK,iBAAiB;AAAA,QAC/B;AAAA,QAEA,UAAmB;AACjB,iBAAO,KAAK,iBAAiB,KAAK,QAAQ;AAAA,QAC5C;AAAA,QAEA,OAAO;AACL,cAAI,CAAC,sBAAsB;AACzB,mBAAO,OAAO,YAAY,EAAE,MAAM,OAAA,GAAU,GAAG;AAAA,UACjD;AAAA,QACF;AAAA,QAEA,OAAO;AACL,cAAI,CAAC,sBAAsB;AACzB,mBAAO,OAAO,YAAY,EAAE,MAAM,OAAA,GAAU,GAAG;AAAA,UACjD;AAAA,QACF;AAAA,QAEA,uBAAuB;AAAA,QAEvB;AAAA,MAAA;AAGF,YAAM,QAAQ,CAAC,CAAC,KAAK,EAAE,MAAM;AAC3B,cAAM,IAAI,IAAI,YAAA;AACd,YAAI,MAAM,SAAS,MAAM,WAAW,MAAM,UAAU,MAAM,QAAQ;AAChE,iBAAO,YAAY,GAAG,EAAE;AAAA,QAC1B;AAAA,MACF,CAAC;AAED,sBAAgB;AAChB,aAAO,cAAc;AACrB,aAAO,eAAe,WAAY;AAAA,MAAC;AAEnC,YAAM,UAAU,MAAM;AACpB,eAAO,cAAc;AACrB,eAAO,eAAe,qBAAA,OAAA,SAAA,kBAAmB;AAGzC,wBAAgB;AAAA,MAClB;AAEA,uBAAiB;AACjB,aAAO;AAAA,IACT;AAAA,IACA,SAAS,MAAM;AACb,WAAK,IAAI,UAAU,YAAY;AAC/BA,kBAAU,IAAI,UAAU,iBAAiB;AACzC,aAAO,oBAAoB,WAAW,SAAS;AAC/C,UAAI,gBAAgB;AAClB,sBAAc,cAAc;AAC5B,yBAAiB;AAAA,MACnB;AACA,uBAAiB,MAAA;AACjB,0BAAoB,MAAA;AACpB,UAAI,gBAAgB;AAClB,uBAAA;AAAA,MACF;AAAA,IACF;AAAA,EAAA;AAEJ;;"}
|
|
@@ -62,6 +62,30 @@ function createIframeBridgeProvider(ydoc, awareness) {
|
|
|
62
62
|
let serverClientId = null;
|
|
63
63
|
let currentCleanup = null;
|
|
64
64
|
let currentMxLike = null;
|
|
65
|
+
let connected = false;
|
|
66
|
+
let initRetryTimer = null;
|
|
67
|
+
const connectListeners = /* @__PURE__ */ new Set();
|
|
68
|
+
const disconnectListeners = /* @__PURE__ */ new Set();
|
|
69
|
+
function setConnected(value) {
|
|
70
|
+
if (connected === value) return;
|
|
71
|
+
connected = value;
|
|
72
|
+
if (value) {
|
|
73
|
+
connectListeners.forEach((fn) => fn());
|
|
74
|
+
} else {
|
|
75
|
+
disconnectListeners.forEach((fn) => fn());
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
function startInitRetry() {
|
|
79
|
+
if (initRetryTimer) {
|
|
80
|
+
clearInterval(initRetryTimer);
|
|
81
|
+
}
|
|
82
|
+
window.parent.postMessage({ type: "init" }, "*");
|
|
83
|
+
initRetryTimer = setInterval(() => {
|
|
84
|
+
if (!connected) {
|
|
85
|
+
window.parent.postMessage({ type: "init" }, "*");
|
|
86
|
+
}
|
|
87
|
+
}, 1e3);
|
|
88
|
+
}
|
|
65
89
|
const onYdocUpdate = (update, origin) => {
|
|
66
90
|
if (applyingParentUpdate) return;
|
|
67
91
|
const isBaseline = origin === null || origin === void 0;
|
|
@@ -99,6 +123,13 @@ function createIframeBridgeProvider(ydoc, awareness) {
|
|
|
99
123
|
applyingParentUpdate = true;
|
|
100
124
|
Y.applyUpdate(ydoc, new Uint8Array(payload));
|
|
101
125
|
applyingParentUpdate = false;
|
|
126
|
+
if (type === "ydoc-sync" && !connected) {
|
|
127
|
+
setConnected(true);
|
|
128
|
+
if (initRetryTimer) {
|
|
129
|
+
clearInterval(initRetryTimer);
|
|
130
|
+
initRetryTimer = null;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
102
133
|
} else if (type === "awareness-sync" || type === "awareness-update") {
|
|
103
134
|
if (receivedServerId != null) {
|
|
104
135
|
serverClientId = receivedServerId;
|
|
@@ -117,19 +148,28 @@ function createIframeBridgeProvider(ydoc, awareness) {
|
|
|
117
148
|
if (newTotal === 0) {
|
|
118
149
|
currentMxLike.fireEvent(createMxEventObject("clear"));
|
|
119
150
|
} else if (newIndex < oldIndex) {
|
|
120
|
-
currentMxLike.fireEvent(
|
|
151
|
+
currentMxLike.fireEvent(
|
|
152
|
+
createMxEventObject("undo", { edit: { changes: [] } })
|
|
153
|
+
);
|
|
121
154
|
} else if (newIndex > oldIndex) {
|
|
122
|
-
currentMxLike.fireEvent(
|
|
155
|
+
currentMxLike.fireEvent(
|
|
156
|
+
createMxEventObject("redo", { edit: { changes: [] } })
|
|
157
|
+
);
|
|
123
158
|
} else {
|
|
124
|
-
currentMxLike.fireEvent(
|
|
159
|
+
currentMxLike.fireEvent(
|
|
160
|
+
createMxEventObject("add", { edit: { changes: [] } })
|
|
161
|
+
);
|
|
125
162
|
}
|
|
126
163
|
applyingParentUpdate = false;
|
|
164
|
+
} else if (type === "disconnect") {
|
|
165
|
+
setConnected(false);
|
|
166
|
+
startInitRetry();
|
|
127
167
|
}
|
|
128
168
|
};
|
|
129
169
|
ydoc.on("update", onYdocUpdate);
|
|
130
170
|
awareness.on("update", onAwarenessUpdate);
|
|
131
171
|
window.addEventListener("message", onMessage);
|
|
132
|
-
|
|
172
|
+
startInitRetry();
|
|
133
173
|
setTimeout(() => {
|
|
134
174
|
window.parent.postMessage({ type: "ping" }, "*");
|
|
135
175
|
}, 100);
|
|
@@ -137,6 +177,26 @@ function createIframeBridgeProvider(ydoc, awareness) {
|
|
|
137
177
|
get serverClientId() {
|
|
138
178
|
return serverClientId;
|
|
139
179
|
},
|
|
180
|
+
get connected() {
|
|
181
|
+
return connected;
|
|
182
|
+
},
|
|
183
|
+
onConnect(fn) {
|
|
184
|
+
connectListeners.add(fn);
|
|
185
|
+
return () => connectListeners.delete(fn);
|
|
186
|
+
},
|
|
187
|
+
onDisconnect(fn) {
|
|
188
|
+
disconnectListeners.add(fn);
|
|
189
|
+
return () => disconnectListeners.delete(fn);
|
|
190
|
+
},
|
|
191
|
+
on(event, fn) {
|
|
192
|
+
if (event === "connect") {
|
|
193
|
+
connectListeners.add(fn);
|
|
194
|
+
return () => connectListeners.delete(fn);
|
|
195
|
+
} else {
|
|
196
|
+
disconnectListeners.add(fn);
|
|
197
|
+
return () => disconnectListeners.delete(fn);
|
|
198
|
+
}
|
|
199
|
+
},
|
|
140
200
|
takeoverUndoManager(file) {
|
|
141
201
|
if (currentCleanup) {
|
|
142
202
|
currentCleanup();
|
|
@@ -216,6 +276,12 @@ function createIframeBridgeProvider(ydoc, awareness) {
|
|
|
216
276
|
ydoc.off("update", onYdocUpdate);
|
|
217
277
|
awareness.off("update", onAwarenessUpdate);
|
|
218
278
|
window.removeEventListener("message", onMessage);
|
|
279
|
+
if (initRetryTimer) {
|
|
280
|
+
clearInterval(initRetryTimer);
|
|
281
|
+
initRetryTimer = null;
|
|
282
|
+
}
|
|
283
|
+
connectListeners.clear();
|
|
284
|
+
disconnectListeners.clear();
|
|
219
285
|
if (currentCleanup) {
|
|
220
286
|
currentCleanup();
|
|
221
287
|
}
|
|
@@ -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 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, origin: unknown) => {\n if (applyingParentUpdate) return;\n // 检测基线数据:origin 为 null 时是 xml2ydoc 首次初始化\n const isBaseline = origin === null || origin === undefined;\n window.parent.postMessage(\n { type: \"ydoc-update\", payload: Array.from(update), isBaseline },\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 // 只发送本地 clientID 的更新给父页面\n // 其他 peers 的更新通过父页面的 WebrtcProvider 同步,不应该从 iframe 回传\n const localClientId = awareness.clientID;\n const localChanged = changes.includes(localClientId);\n if (!localChanged) return;\n\n const update = encodeAwarenessUpdate(awareness, [localClientId]);\n const remapped =\n serverClientId != null\n ? remapClientIdInUpdate(update, localClientId, 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 // 直接使用 server 发送的原始 clientID,不做映射\n // iframe 的 awareness 中会包含:\n // - iframe 自身的 clientID (awareness.clientID)\n // - server 的 clientID (serverClientId)\n // - 其他 Webrtc peers 的 clientID\n applyingParentUpdate = true;\n applyAwarenessUpdate(awareness, new Uint8Array(payload), null);\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(createMxEventObject(\"undo\", { edit: { changes: [] } }));\n } else if (newIndex > oldIndex) {\n currentMxLike.fireEvent(createMxEventObject(\"redo\", { edit: { changes: [] } }));\n } else {\n currentMxLike.fireEvent(createMxEventObject(\"add\", { edit: { changes: [] } }));\n }\n\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 // 发送 ping 获取 serverClientId\n setTimeout(() => {\n window.parent.postMessage({ type: \"ping\" }, \"*\");\n }, 100);\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 \"\");\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 (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\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 connected: boolean;\n onConnect: (fn: () => void) => () => void;\n onDisconnect: (fn: () => void) => () => void;\n on: (event: \"connect\" | \"disconnect\", fn: () => void) => () => 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 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 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 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 window.parent.postMessage({ type: \"init\" }, \"*\");\n initRetryTimer = setInterval(() => {\n if (!connected) {\n window.parent.postMessage({ 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 window.parent.postMessage(\n { type: \"ydoc-update\", payload: Array.from(update), isBaseline },\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 // 只发送本地 clientID 的更新给父页面\n // 其他 peers 的更新通过父页面的 WebrtcProvider 同步,不应该从 iframe 回传\n const localClientId = awareness.clientID;\n const localChanged = changes.includes(localClientId);\n if (!localChanged) return;\n\n const update = encodeAwarenessUpdate(awareness, [localClientId]);\n const remapped =\n serverClientId != null\n ? remapClientIdInUpdate(update, localClientId, 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 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 if (receivedServerId != null) {\n serverClientId = receivedServerId;\n }\n\n // 直接使用 server 发送的原始 clientID,不做映射\n // iframe 的 awareness 中会包含:\n // - iframe 自身的 clientID (awareness.clientID)\n // - server 的 clientID (serverClientId)\n // - 其他 Webrtc peers 的 clientID\n applyingParentUpdate = true;\n applyAwarenessUpdate(awareness, new Uint8Array(payload), null);\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 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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"}
|
package/iframe-bridge/server.cjs
CHANGED
|
@@ -23,11 +23,26 @@ const IFRAME_ORIGIN = {};
|
|
|
23
23
|
const BASELINE_ORIGIN = {};
|
|
24
24
|
function createIframeBridgeServer(iframe, ydoc, awareness$1, options) {
|
|
25
25
|
const { undoManager } = options != null ? options : {};
|
|
26
|
+
let connected = false;
|
|
26
27
|
let applyingIframeUpdate = false;
|
|
28
|
+
const connectListeners = /* @__PURE__ */ new Set();
|
|
29
|
+
const disconnectListeners = /* @__PURE__ */ new Set();
|
|
30
|
+
function setConnected(value) {
|
|
31
|
+
if (connected === value) return;
|
|
32
|
+
connected = value;
|
|
33
|
+
if (value) {
|
|
34
|
+
connectListeners.forEach((fn) => fn());
|
|
35
|
+
} else {
|
|
36
|
+
disconnectListeners.forEach((fn) => fn());
|
|
37
|
+
}
|
|
38
|
+
}
|
|
27
39
|
function postToIframe(type, payload) {
|
|
28
40
|
const cw = iframe.contentWindow;
|
|
29
41
|
if (cw) {
|
|
30
|
-
cw.postMessage(
|
|
42
|
+
cw.postMessage(
|
|
43
|
+
{ type, payload: payload ? Array.from(payload) : [] },
|
|
44
|
+
"*"
|
|
45
|
+
);
|
|
31
46
|
}
|
|
32
47
|
}
|
|
33
48
|
function postUndoStateToIframe() {
|
|
@@ -66,7 +81,16 @@ function createIframeBridgeServer(iframe, ydoc, awareness$1, options) {
|
|
|
66
81
|
if (event.source !== iframe.contentWindow) return;
|
|
67
82
|
const { type: msgType, payload } = event.data;
|
|
68
83
|
if (msgType === "init") {
|
|
84
|
+
console.log(
|
|
85
|
+
`[iframe-bridge server] received init — connected=${connected}`
|
|
86
|
+
);
|
|
87
|
+
if (!connected) {
|
|
88
|
+
setConnected(true);
|
|
89
|
+
}
|
|
69
90
|
const docState = Y__namespace.encodeStateAsUpdate(ydoc);
|
|
91
|
+
console.log(
|
|
92
|
+
`[iframe-bridge server] sending ydoc-sync — docState bytes=${docState.length}`
|
|
93
|
+
);
|
|
70
94
|
const awarenessState = awareness.encodeAwarenessUpdate(
|
|
71
95
|
awareness$1,
|
|
72
96
|
Array.from(awareness$1.getStates().keys())
|
|
@@ -75,7 +99,11 @@ function createIframeBridgeServer(iframe, ydoc, awareness$1, options) {
|
|
|
75
99
|
const cw = iframe.contentWindow;
|
|
76
100
|
if (cw) {
|
|
77
101
|
cw.postMessage(
|
|
78
|
-
{
|
|
102
|
+
{
|
|
103
|
+
type: "awareness-sync",
|
|
104
|
+
payload: Array.from(awarenessState),
|
|
105
|
+
serverClientId: awareness$1.clientID
|
|
106
|
+
},
|
|
79
107
|
"*"
|
|
80
108
|
);
|
|
81
109
|
}
|
|
@@ -123,7 +151,29 @@ function createIframeBridgeServer(iframe, ydoc, awareness$1, options) {
|
|
|
123
151
|
undoManager.on("stack-item-added", onStackItemAdded);
|
|
124
152
|
}
|
|
125
153
|
return {
|
|
154
|
+
get connected() {
|
|
155
|
+
return connected;
|
|
156
|
+
},
|
|
157
|
+
onConnect(fn) {
|
|
158
|
+
connectListeners.add(fn);
|
|
159
|
+
return () => connectListeners.delete(fn);
|
|
160
|
+
},
|
|
161
|
+
onDisconnect(fn) {
|
|
162
|
+
disconnectListeners.add(fn);
|
|
163
|
+
return () => disconnectListeners.delete(fn);
|
|
164
|
+
},
|
|
165
|
+
on(event, fn) {
|
|
166
|
+
if (event === "connect") {
|
|
167
|
+
connectListeners.add(fn);
|
|
168
|
+
return () => connectListeners.delete(fn);
|
|
169
|
+
} else {
|
|
170
|
+
disconnectListeners.add(fn);
|
|
171
|
+
return () => disconnectListeners.delete(fn);
|
|
172
|
+
}
|
|
173
|
+
},
|
|
126
174
|
destroy: () => {
|
|
175
|
+
setConnected(false);
|
|
176
|
+
postToIframe("disconnect");
|
|
127
177
|
ydoc.off("update", onYdocUpdate);
|
|
128
178
|
awareness$1.off("update", onAwarenessUpdate);
|
|
129
179
|
window.removeEventListener("message", onMessage);
|
|
@@ -132,6 +182,8 @@ function createIframeBridgeServer(iframe, ydoc, awareness$1, options) {
|
|
|
132
182
|
undoManager.off("stack-cleared", onStackCleared);
|
|
133
183
|
undoManager.off("stack-item-added", onStackItemAdded);
|
|
134
184
|
}
|
|
185
|
+
connectListeners.clear();
|
|
186
|
+
disconnectListeners.clear();
|
|
135
187
|
}
|
|
136
188
|
};
|
|
137
189
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.cjs","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\n/**\n * 基线数据标记(用于首次初始化)。\n * 此 origin 的更新不应进入 UndoManager 的撤销栈。\n */\nexport const BASELINE_ORIGIN: object = {};\n","import * as Y from \"yjs\";\nimport {\n Awareness,\n applyAwarenessUpdate,\n encodeAwarenessUpdate,\n} from \"y-protocols/awareness\";\nimport { IFRAME_ORIGIN, BASELINE_ORIGIN } from \"./origin.js\";\n\nexport interface IframeBridgeServerOptions {\n undoManager?: Y.UndoManager;\n}\n\nexport interface IframeBridgeServer {\n destroy: () => void;\n}\n\nexport function createIframeBridgeServer(\n iframe: HTMLIFrameElement,\n ydoc: Y.Doc,\n awareness: Awareness,\n options?: IframeBridgeServerOptions,\n): IframeBridgeServer {\n const { undoManager } = options ?? {};\n let iframeReady = false;\n let applyingIframeUpdate = false;\n\n function postToIframe(type: string, payload?: Uint8Array) {\n const cw = iframe.contentWindow;\n if (cw) {\n cw.postMessage({ type, payload: payload ? Array.from(payload) : [] }, \"*\");\n }\n }\n\n function postUndoStateToIframe() {\n if (!undoManager) return;\n const undoStack = (undoManager as any).undoStack;\n const redoStack = (undoManager as any).redoStack;\n const cw = iframe.contentWindow;\n if (cw) {\n const state = {\n type: \"undo-state\",\n canUndo: undoManager.canUndo(),\n canRedo: undoManager.canRedo(),\n undoStackSize: undoStack?.length ?? 0,\n redoStackSize: redoStack?.length ?? 0,\n };\n cw.postMessage(state, \"*\");\n }\n }\n\n const onYdocUpdate = (update: Uint8Array, origin: unknown) => {\n if (origin === IFRAME_ORIGIN) return;\n postToIframe(\"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 if (applyingIframeUpdate) return;\n const changes = [...added, ...updated, ...removed];\n if (changes.length === 0) return;\n\n // 把所有变化的 clientID 都发送给 iframe(包括其他 Webrtc peers 的光标更新)\n // 但要注意:server 自身的 clientID 需要被 iframe 识别为 serverClientId\n const update = encodeAwarenessUpdate(awareness, changes);\n postToIframe(\"awareness-update\", update);\n };\n\n const onMessage = (event: MessageEvent) => {\n if (event.source !== iframe.contentWindow) return;\n\n const { type: msgType, payload } = event.data;\n\n if (msgType === \"init\") {\n if (!iframeReady) {\n iframeReady = true;\n }\n const docState = Y.encodeStateAsUpdate(ydoc);\n const awarenessState = encodeAwarenessUpdate(\n awareness,\n Array.from(awareness.getStates().keys()),\n );\n postToIframe(\"ydoc-sync\", new Uint8Array(Array.from(docState)));\n // 在单独的 postMessage 中发送 serverClientId,方便 iframe 接收\n const cw = iframe.contentWindow;\n if (cw) {\n cw.postMessage(\n { type: \"awareness-sync\", payload: Array.from(awarenessState), serverClientId: awareness.clientID },\n \"*\",\n );\n }\n // 同步初始 undo 状态\n postUndoStateToIframe();\n } else if (msgType === \"ping\") {\n const cw = iframe.contentWindow;\n if (cw) {\n cw.postMessage(\n { type: \"pong\", serverClientId: awareness.clientID },\n \"*\",\n );\n }\n } else if (msgType === \"ydoc-update\") {\n const update = new Uint8Array(payload);\n const isBaseline = event.data.isBaseline;\n // 基线数据使用 BASELINE_ORIGIN(不进入 undo 栈),编辑数据使用 IFRAME_ORIGIN\n const applyOrigin = isBaseline ? BASELINE_ORIGIN : IFRAME_ORIGIN;\n Y.applyUpdate(ydoc, update, applyOrigin);\n // 源 iframe 已经持有此 update,无需回传\n } else if (msgType === \"awareness-update\") {\n // 应用 iframe 的 awareness 更新时设置标志,防止触发 onAwarenessUpdate 回传\n applyingIframeUpdate = true;\n applyAwarenessUpdate(awareness, new Uint8Array(payload), IFRAME_ORIGIN);\n applyingIframeUpdate = false;\n } else if (msgType === \"undo\" && undoManager) {\n undoManager.undo();\n postUndoStateToIframe();\n } else if (msgType === \"redo\" && undoManager) {\n undoManager.redo();\n postUndoStateToIframe();\n }\n };\n\n const onUndoPopped = () => {\n postUndoStateToIframe();\n };\n\n const onStackCleared = () => {\n postUndoStateToIframe();\n };\n\n const onStackItemAdded = () => {\n postUndoStateToIframe();\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 undoManager.on(\"stack-item-added\", onStackItemAdded);\n }\n\n return {\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 undoManager.off(\"stack-item-added\", onStackItemAdded);\n }\n iframeReady = false;\n },\n };\n}\n"],"names":["awareness","encodeAwarenessUpdate","Y","applyAwarenessUpdate"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"server.cjs","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\n/**\n * 基线数据标记(用于首次初始化)。\n * 此 origin 的更新不应进入 UndoManager 的撤销栈。\n */\nexport const BASELINE_ORIGIN: object = {};\n","import * as Y from \"yjs\";\nimport {\n Awareness,\n applyAwarenessUpdate,\n encodeAwarenessUpdate,\n} from \"y-protocols/awareness\";\nimport { IFRAME_ORIGIN, BASELINE_ORIGIN } from \"./origin.js\";\n\nexport interface IframeBridgeServerOptions {\n undoManager?: Y.UndoManager;\n}\n\nexport interface IframeBridgeServer {\n connected: boolean;\n onConnect: (fn: () => void) => () => void;\n onDisconnect: (fn: () => void) => () => void;\n on: (event: \"connect\" | \"disconnect\", fn: () => void) => () => void;\n destroy: () => void;\n}\n\nexport function createIframeBridgeServer(\n iframe: HTMLIFrameElement,\n ydoc: Y.Doc,\n awareness: Awareness,\n options?: IframeBridgeServerOptions,\n): IframeBridgeServer {\n const { undoManager } = options ?? {};\n let connected = false;\n let applyingIframeUpdate = false;\n const connectListeners = new Set<() => void>();\n const disconnectListeners = new Set<() => void>();\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 postToIframe(type: string, payload?: Uint8Array) {\n const cw = iframe.contentWindow;\n if (cw) {\n cw.postMessage(\n { type, payload: payload ? Array.from(payload) : [] },\n \"*\",\n );\n }\n }\n\n function postUndoStateToIframe() {\n if (!undoManager) return;\n const undoStack = (undoManager as any).undoStack;\n const redoStack = (undoManager as any).redoStack;\n const cw = iframe.contentWindow;\n if (cw) {\n const state = {\n type: \"undo-state\",\n canUndo: undoManager.canUndo(),\n canRedo: undoManager.canRedo(),\n undoStackSize: undoStack?.length ?? 0,\n redoStackSize: redoStack?.length ?? 0,\n };\n cw.postMessage(state, \"*\");\n }\n }\n\n const onYdocUpdate = (update: Uint8Array, origin: unknown) => {\n if (origin === IFRAME_ORIGIN) return;\n postToIframe(\"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 if (applyingIframeUpdate) return;\n const changes = [...added, ...updated, ...removed];\n if (changes.length === 0) return;\n\n // 把所有变化的 clientID 都发送给 iframe(包括其他 Webrtc peers 的光标更新)\n // 但要注意:server 自身的 clientID 需要被 iframe 识别为 serverClientId\n const update = encodeAwarenessUpdate(awareness, changes);\n postToIframe(\"awareness-update\", update);\n };\n\n const onMessage = (event: MessageEvent) => {\n if (event.source !== iframe.contentWindow) return;\n\n const { type: msgType, payload } = event.data;\n\n if (msgType === \"init\") {\n console.log(\n `[iframe-bridge server] received init — connected=${connected}`,\n );\n if (!connected) {\n setConnected(true);\n }\n const docState = Y.encodeStateAsUpdate(ydoc);\n console.log(\n `[iframe-bridge server] sending ydoc-sync — docState bytes=${docState.length}`,\n );\n const awarenessState = encodeAwarenessUpdate(\n awareness,\n Array.from(awareness.getStates().keys()),\n );\n postToIframe(\"ydoc-sync\", new Uint8Array(Array.from(docState)));\n // 在单独的 postMessage 中发送 serverClientId,方便 iframe 接收\n const cw = iframe.contentWindow;\n if (cw) {\n cw.postMessage(\n {\n type: \"awareness-sync\",\n payload: Array.from(awarenessState),\n serverClientId: awareness.clientID,\n },\n \"*\",\n );\n }\n // 同步初始 undo 状态\n postUndoStateToIframe();\n } else if (msgType === \"ping\") {\n const cw = iframe.contentWindow;\n if (cw) {\n cw.postMessage(\n { type: \"pong\", serverClientId: awareness.clientID },\n \"*\",\n );\n }\n } else if (msgType === \"ydoc-update\") {\n const update = new Uint8Array(payload);\n const isBaseline = event.data.isBaseline;\n // 基线数据使用 BASELINE_ORIGIN(不进入 undo 栈),编辑数据使用 IFRAME_ORIGIN\n const applyOrigin = isBaseline ? BASELINE_ORIGIN : IFRAME_ORIGIN;\n Y.applyUpdate(ydoc, update, applyOrigin);\n // 源 iframe 已经持有此 update,无需回传\n } else if (msgType === \"awareness-update\") {\n // 应用 iframe 的 awareness 更新时设置标志,防止触发 onAwarenessUpdate 回传\n applyingIframeUpdate = true;\n applyAwarenessUpdate(awareness, new Uint8Array(payload), IFRAME_ORIGIN);\n applyingIframeUpdate = false;\n } else if (msgType === \"undo\" && undoManager) {\n undoManager.undo();\n postUndoStateToIframe();\n } else if (msgType === \"redo\" && undoManager) {\n undoManager.redo();\n postUndoStateToIframe();\n }\n };\n\n const onUndoPopped = () => {\n postUndoStateToIframe();\n };\n\n const onStackCleared = () => {\n postUndoStateToIframe();\n };\n\n const onStackItemAdded = () => {\n postUndoStateToIframe();\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 undoManager.on(\"stack-item-added\", onStackItemAdded);\n }\n\n return {\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 destroy: () => {\n setConnected(false);\n postToIframe(\"disconnect\");\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 undoManager.off(\"stack-item-added\", onStackItemAdded);\n }\n connectListeners.clear();\n disconnectListeners.clear();\n },\n };\n}\n"],"names":["awareness","encodeAwarenessUpdate","Y","applyAwarenessUpdate"],"mappings":"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"}
|
package/iframe-bridge/server.js
CHANGED
|
@@ -4,11 +4,26 @@ const IFRAME_ORIGIN = {};
|
|
|
4
4
|
const BASELINE_ORIGIN = {};
|
|
5
5
|
function createIframeBridgeServer(iframe, ydoc, awareness, options) {
|
|
6
6
|
const { undoManager } = options != null ? options : {};
|
|
7
|
+
let connected = false;
|
|
7
8
|
let applyingIframeUpdate = false;
|
|
9
|
+
const connectListeners = /* @__PURE__ */ new Set();
|
|
10
|
+
const disconnectListeners = /* @__PURE__ */ new Set();
|
|
11
|
+
function setConnected(value) {
|
|
12
|
+
if (connected === value) return;
|
|
13
|
+
connected = value;
|
|
14
|
+
if (value) {
|
|
15
|
+
connectListeners.forEach((fn) => fn());
|
|
16
|
+
} else {
|
|
17
|
+
disconnectListeners.forEach((fn) => fn());
|
|
18
|
+
}
|
|
19
|
+
}
|
|
8
20
|
function postToIframe(type, payload) {
|
|
9
21
|
const cw = iframe.contentWindow;
|
|
10
22
|
if (cw) {
|
|
11
|
-
cw.postMessage(
|
|
23
|
+
cw.postMessage(
|
|
24
|
+
{ type, payload: payload ? Array.from(payload) : [] },
|
|
25
|
+
"*"
|
|
26
|
+
);
|
|
12
27
|
}
|
|
13
28
|
}
|
|
14
29
|
function postUndoStateToIframe() {
|
|
@@ -47,7 +62,16 @@ function createIframeBridgeServer(iframe, ydoc, awareness, options) {
|
|
|
47
62
|
if (event.source !== iframe.contentWindow) return;
|
|
48
63
|
const { type: msgType, payload } = event.data;
|
|
49
64
|
if (msgType === "init") {
|
|
65
|
+
console.log(
|
|
66
|
+
`[iframe-bridge server] received init — connected=${connected}`
|
|
67
|
+
);
|
|
68
|
+
if (!connected) {
|
|
69
|
+
setConnected(true);
|
|
70
|
+
}
|
|
50
71
|
const docState = Y.encodeStateAsUpdate(ydoc);
|
|
72
|
+
console.log(
|
|
73
|
+
`[iframe-bridge server] sending ydoc-sync — docState bytes=${docState.length}`
|
|
74
|
+
);
|
|
51
75
|
const awarenessState = encodeAwarenessUpdate(
|
|
52
76
|
awareness,
|
|
53
77
|
Array.from(awareness.getStates().keys())
|
|
@@ -56,7 +80,11 @@ function createIframeBridgeServer(iframe, ydoc, awareness, options) {
|
|
|
56
80
|
const cw = iframe.contentWindow;
|
|
57
81
|
if (cw) {
|
|
58
82
|
cw.postMessage(
|
|
59
|
-
{
|
|
83
|
+
{
|
|
84
|
+
type: "awareness-sync",
|
|
85
|
+
payload: Array.from(awarenessState),
|
|
86
|
+
serverClientId: awareness.clientID
|
|
87
|
+
},
|
|
60
88
|
"*"
|
|
61
89
|
);
|
|
62
90
|
}
|
|
@@ -104,7 +132,29 @@ function createIframeBridgeServer(iframe, ydoc, awareness, options) {
|
|
|
104
132
|
undoManager.on("stack-item-added", onStackItemAdded);
|
|
105
133
|
}
|
|
106
134
|
return {
|
|
135
|
+
get connected() {
|
|
136
|
+
return connected;
|
|
137
|
+
},
|
|
138
|
+
onConnect(fn) {
|
|
139
|
+
connectListeners.add(fn);
|
|
140
|
+
return () => connectListeners.delete(fn);
|
|
141
|
+
},
|
|
142
|
+
onDisconnect(fn) {
|
|
143
|
+
disconnectListeners.add(fn);
|
|
144
|
+
return () => disconnectListeners.delete(fn);
|
|
145
|
+
},
|
|
146
|
+
on(event, fn) {
|
|
147
|
+
if (event === "connect") {
|
|
148
|
+
connectListeners.add(fn);
|
|
149
|
+
return () => connectListeners.delete(fn);
|
|
150
|
+
} else {
|
|
151
|
+
disconnectListeners.add(fn);
|
|
152
|
+
return () => disconnectListeners.delete(fn);
|
|
153
|
+
}
|
|
154
|
+
},
|
|
107
155
|
destroy: () => {
|
|
156
|
+
setConnected(false);
|
|
157
|
+
postToIframe("disconnect");
|
|
108
158
|
ydoc.off("update", onYdocUpdate);
|
|
109
159
|
awareness.off("update", onAwarenessUpdate);
|
|
110
160
|
window.removeEventListener("message", onMessage);
|
|
@@ -113,6 +163,8 @@ function createIframeBridgeServer(iframe, ydoc, awareness, options) {
|
|
|
113
163
|
undoManager.off("stack-cleared", onStackCleared);
|
|
114
164
|
undoManager.off("stack-item-added", onStackItemAdded);
|
|
115
165
|
}
|
|
166
|
+
connectListeners.clear();
|
|
167
|
+
disconnectListeners.clear();
|
|
116
168
|
}
|
|
117
169
|
};
|
|
118
170
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.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\n/**\n * 基线数据标记(用于首次初始化)。\n * 此 origin 的更新不应进入 UndoManager 的撤销栈。\n */\nexport const BASELINE_ORIGIN: object = {};\n","import * as Y from \"yjs\";\nimport {\n Awareness,\n applyAwarenessUpdate,\n encodeAwarenessUpdate,\n} from \"y-protocols/awareness\";\nimport { IFRAME_ORIGIN, BASELINE_ORIGIN } from \"./origin.js\";\n\nexport interface IframeBridgeServerOptions {\n undoManager?: Y.UndoManager;\n}\n\nexport interface IframeBridgeServer {\n destroy: () => void;\n}\n\nexport function createIframeBridgeServer(\n iframe: HTMLIFrameElement,\n ydoc: Y.Doc,\n awareness: Awareness,\n options?: IframeBridgeServerOptions,\n): IframeBridgeServer {\n const { undoManager } = options ?? {};\n let iframeReady = false;\n let applyingIframeUpdate = false;\n\n function postToIframe(type: string, payload?: Uint8Array) {\n const cw = iframe.contentWindow;\n if (cw) {\n cw.postMessage({ type, payload: payload ? Array.from(payload) : [] }, \"*\");\n }\n }\n\n function postUndoStateToIframe() {\n if (!undoManager) return;\n const undoStack = (undoManager as any).undoStack;\n const redoStack = (undoManager as any).redoStack;\n const cw = iframe.contentWindow;\n if (cw) {\n const state = {\n type: \"undo-state\",\n canUndo: undoManager.canUndo(),\n canRedo: undoManager.canRedo(),\n undoStackSize: undoStack?.length ?? 0,\n redoStackSize: redoStack?.length ?? 0,\n };\n cw.postMessage(state, \"*\");\n }\n }\n\n const onYdocUpdate = (update: Uint8Array, origin: unknown) => {\n if (origin === IFRAME_ORIGIN) return;\n postToIframe(\"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 if (applyingIframeUpdate) return;\n const changes = [...added, ...updated, ...removed];\n if (changes.length === 0) return;\n\n // 把所有变化的 clientID 都发送给 iframe(包括其他 Webrtc peers 的光标更新)\n // 但要注意:server 自身的 clientID 需要被 iframe 识别为 serverClientId\n const update = encodeAwarenessUpdate(awareness, changes);\n postToIframe(\"awareness-update\", update);\n };\n\n const onMessage = (event: MessageEvent) => {\n if (event.source !== iframe.contentWindow) return;\n\n const { type: msgType, payload } = event.data;\n\n if (msgType === \"init\") {\n if (!iframeReady) {\n iframeReady = true;\n }\n const docState = Y.encodeStateAsUpdate(ydoc);\n const awarenessState = encodeAwarenessUpdate(\n awareness,\n Array.from(awareness.getStates().keys()),\n );\n postToIframe(\"ydoc-sync\", new Uint8Array(Array.from(docState)));\n // 在单独的 postMessage 中发送 serverClientId,方便 iframe 接收\n const cw = iframe.contentWindow;\n if (cw) {\n cw.postMessage(\n { type: \"awareness-sync\", payload: Array.from(awarenessState), serverClientId: awareness.clientID },\n \"*\",\n );\n }\n // 同步初始 undo 状态\n postUndoStateToIframe();\n } else if (msgType === \"ping\") {\n const cw = iframe.contentWindow;\n if (cw) {\n cw.postMessage(\n { type: \"pong\", serverClientId: awareness.clientID },\n \"*\",\n );\n }\n } else if (msgType === \"ydoc-update\") {\n const update = new Uint8Array(payload);\n const isBaseline = event.data.isBaseline;\n // 基线数据使用 BASELINE_ORIGIN(不进入 undo 栈),编辑数据使用 IFRAME_ORIGIN\n const applyOrigin = isBaseline ? BASELINE_ORIGIN : IFRAME_ORIGIN;\n Y.applyUpdate(ydoc, update, applyOrigin);\n // 源 iframe 已经持有此 update,无需回传\n } else if (msgType === \"awareness-update\") {\n // 应用 iframe 的 awareness 更新时设置标志,防止触发 onAwarenessUpdate 回传\n applyingIframeUpdate = true;\n applyAwarenessUpdate(awareness, new Uint8Array(payload), IFRAME_ORIGIN);\n applyingIframeUpdate = false;\n } else if (msgType === \"undo\" && undoManager) {\n undoManager.undo();\n postUndoStateToIframe();\n } else if (msgType === \"redo\" && undoManager) {\n undoManager.redo();\n postUndoStateToIframe();\n }\n };\n\n const onUndoPopped = () => {\n postUndoStateToIframe();\n };\n\n const onStackCleared = () => {\n postUndoStateToIframe();\n };\n\n const onStackItemAdded = () => {\n postUndoStateToIframe();\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 undoManager.on(\"stack-item-added\", onStackItemAdded);\n }\n\n return {\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 undoManager.off(\"stack-item-added\", onStackItemAdded);\n }\n iframeReady = false;\n },\n };\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"server.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\n/**\n * 基线数据标记(用于首次初始化)。\n * 此 origin 的更新不应进入 UndoManager 的撤销栈。\n */\nexport const BASELINE_ORIGIN: object = {};\n","import * as Y from \"yjs\";\nimport {\n Awareness,\n applyAwarenessUpdate,\n encodeAwarenessUpdate,\n} from \"y-protocols/awareness\";\nimport { IFRAME_ORIGIN, BASELINE_ORIGIN } from \"./origin.js\";\n\nexport interface IframeBridgeServerOptions {\n undoManager?: Y.UndoManager;\n}\n\nexport interface IframeBridgeServer {\n connected: boolean;\n onConnect: (fn: () => void) => () => void;\n onDisconnect: (fn: () => void) => () => void;\n on: (event: \"connect\" | \"disconnect\", fn: () => void) => () => void;\n destroy: () => void;\n}\n\nexport function createIframeBridgeServer(\n iframe: HTMLIFrameElement,\n ydoc: Y.Doc,\n awareness: Awareness,\n options?: IframeBridgeServerOptions,\n): IframeBridgeServer {\n const { undoManager } = options ?? {};\n let connected = false;\n let applyingIframeUpdate = false;\n const connectListeners = new Set<() => void>();\n const disconnectListeners = new Set<() => void>();\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 postToIframe(type: string, payload?: Uint8Array) {\n const cw = iframe.contentWindow;\n if (cw) {\n cw.postMessage(\n { type, payload: payload ? Array.from(payload) : [] },\n \"*\",\n );\n }\n }\n\n function postUndoStateToIframe() {\n if (!undoManager) return;\n const undoStack = (undoManager as any).undoStack;\n const redoStack = (undoManager as any).redoStack;\n const cw = iframe.contentWindow;\n if (cw) {\n const state = {\n type: \"undo-state\",\n canUndo: undoManager.canUndo(),\n canRedo: undoManager.canRedo(),\n undoStackSize: undoStack?.length ?? 0,\n redoStackSize: redoStack?.length ?? 0,\n };\n cw.postMessage(state, \"*\");\n }\n }\n\n const onYdocUpdate = (update: Uint8Array, origin: unknown) => {\n if (origin === IFRAME_ORIGIN) return;\n postToIframe(\"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 if (applyingIframeUpdate) return;\n const changes = [...added, ...updated, ...removed];\n if (changes.length === 0) return;\n\n // 把所有变化的 clientID 都发送给 iframe(包括其他 Webrtc peers 的光标更新)\n // 但要注意:server 自身的 clientID 需要被 iframe 识别为 serverClientId\n const update = encodeAwarenessUpdate(awareness, changes);\n postToIframe(\"awareness-update\", update);\n };\n\n const onMessage = (event: MessageEvent) => {\n if (event.source !== iframe.contentWindow) return;\n\n const { type: msgType, payload } = event.data;\n\n if (msgType === \"init\") {\n console.log(\n `[iframe-bridge server] received init — connected=${connected}`,\n );\n if (!connected) {\n setConnected(true);\n }\n const docState = Y.encodeStateAsUpdate(ydoc);\n console.log(\n `[iframe-bridge server] sending ydoc-sync — docState bytes=${docState.length}`,\n );\n const awarenessState = encodeAwarenessUpdate(\n awareness,\n Array.from(awareness.getStates().keys()),\n );\n postToIframe(\"ydoc-sync\", new Uint8Array(Array.from(docState)));\n // 在单独的 postMessage 中发送 serverClientId,方便 iframe 接收\n const cw = iframe.contentWindow;\n if (cw) {\n cw.postMessage(\n {\n type: \"awareness-sync\",\n payload: Array.from(awarenessState),\n serverClientId: awareness.clientID,\n },\n \"*\",\n );\n }\n // 同步初始 undo 状态\n postUndoStateToIframe();\n } else if (msgType === \"ping\") {\n const cw = iframe.contentWindow;\n if (cw) {\n cw.postMessage(\n { type: \"pong\", serverClientId: awareness.clientID },\n \"*\",\n );\n }\n } else if (msgType === \"ydoc-update\") {\n const update = new Uint8Array(payload);\n const isBaseline = event.data.isBaseline;\n // 基线数据使用 BASELINE_ORIGIN(不进入 undo 栈),编辑数据使用 IFRAME_ORIGIN\n const applyOrigin = isBaseline ? BASELINE_ORIGIN : IFRAME_ORIGIN;\n Y.applyUpdate(ydoc, update, applyOrigin);\n // 源 iframe 已经持有此 update,无需回传\n } else if (msgType === \"awareness-update\") {\n // 应用 iframe 的 awareness 更新时设置标志,防止触发 onAwarenessUpdate 回传\n applyingIframeUpdate = true;\n applyAwarenessUpdate(awareness, new Uint8Array(payload), IFRAME_ORIGIN);\n applyingIframeUpdate = false;\n } else if (msgType === \"undo\" && undoManager) {\n undoManager.undo();\n postUndoStateToIframe();\n } else if (msgType === \"redo\" && undoManager) {\n undoManager.redo();\n postUndoStateToIframe();\n }\n };\n\n const onUndoPopped = () => {\n postUndoStateToIframe();\n };\n\n const onStackCleared = () => {\n postUndoStateToIframe();\n };\n\n const onStackItemAdded = () => {\n postUndoStateToIframe();\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 undoManager.on(\"stack-item-added\", onStackItemAdded);\n }\n\n return {\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 destroy: () => {\n setConnected(false);\n postToIframe(\"disconnect\");\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 undoManager.off(\"stack-item-added\", onStackItemAdded);\n }\n connectListeners.clear();\n disconnectListeners.clear();\n },\n };\n}\n"],"names":[],"mappings":";;AAKO,MAAM,gBAAwB,CAAA;AAM9B,MAAM,kBAA0B,CAAA;ACShC,SAAS,yBACd,QACA,MACA,WACA,SACoB;AACpB,QAAM,EAAE,YAAA,IAAgB,WAAA,OAAA,UAAW,CAAA;AACnC,MAAI,YAAY;AAChB,MAAI,uBAAuB;AAC3B,QAAM,uCAAuB,IAAA;AAC7B,QAAM,0CAA0B,IAAA;AAEhC,WAAS,aAAa,OAAgB;AACpC,QAAI,cAAc,MAAO;AACzB,gBAAY;AACZ,QAAI,OAAO;AACT,uBAAiB,QAAQ,CAAC,OAAO,GAAA,CAAI;AAAA,IACvC,OAAO;AACL,0BAAoB,QAAQ,CAAC,OAAO,GAAA,CAAI;AAAA,IAC1C;AAAA,EACF;AAEA,WAAS,aAAa,MAAc,SAAsB;AACxD,UAAM,KAAK,OAAO;AAClB,QAAI,IAAI;AACN,SAAG;AAAA,QACD,EAAE,MAAM,SAAS,UAAU,MAAM,KAAK,OAAO,IAAI,GAAC;AAAA,QAClD;AAAA,MAAA;AAAA,IAEJ;AAAA,EACF;AAEA,WAAS,wBAAwB;AApDnC,QAAA,IAAA;AAqDI,QAAI,CAAC,YAAa;AAClB,UAAM,YAAa,YAAoB;AACvC,UAAM,YAAa,YAAoB;AACvC,UAAM,KAAK,OAAO;AAClB,QAAI,IAAI;AACN,YAAM,QAAQ;AAAA,QACZ,MAAM;AAAA,QACN,SAAS,YAAY,QAAA;AAAA,QACrB,SAAS,YAAY,QAAA;AAAA,QACrB,gBAAe,KAAA,aAAA,OAAA,SAAA,UAAW,WAAX,OAAA,KAAqB;AAAA,QACpC,gBAAe,KAAA,aAAA,OAAA,SAAA,UAAW,WAAX,OAAA,KAAqB;AAAA,MAAA;AAEtC,SAAG,YAAY,OAAO,GAAG;AAAA,IAC3B;AAAA,EACF;AAEA,QAAM,eAAe,CAAC,QAAoB,WAAoB;AAC5D,QAAI,WAAW,cAAe;AAC9B,iBAAa,eAAe,MAAM;AAAA,EACpC;AAEA,QAAM,oBAAoB,CAAC;AAAA,IACzB;AAAA,IACA;AAAA,IACA;AAAA,EAAA,MAKI;AACJ,QAAI,qBAAsB;AAC1B,UAAM,UAAU,CAAC,GAAG,OAAO,GAAG,SAAS,GAAG,OAAO;AACjD,QAAI,QAAQ,WAAW,EAAG;AAI1B,UAAM,SAAS,sBAAsB,WAAW,OAAO;AACvD,iBAAa,oBAAoB,MAAM;AAAA,EACzC;AAEA,QAAM,YAAY,CAAC,UAAwB;AACzC,QAAI,MAAM,WAAW,OAAO,cAAe;AAE3C,UAAM,EAAE,MAAM,SAAS,QAAA,IAAY,MAAM;AAEzC,QAAI,YAAY,QAAQ;AACtB,cAAQ;AAAA,QACN,oDAAoD,SAAS;AAAA,MAAA;AAE/D,UAAI,CAAC,WAAW;AACd,qBAAa,IAAI;AAAA,MACnB;AACA,YAAM,WAAW,EAAE,oBAAoB,IAAI;AAC3C,cAAQ;AAAA,QACN,6DAA6D,SAAS,MAAM;AAAA,MAAA;AAE9E,YAAM,iBAAiB;AAAA,QACrB;AAAA,QACA,MAAM,KAAK,UAAU,UAAA,EAAY,MAAM;AAAA,MAAA;AAEzC,mBAAa,aAAa,IAAI,WAAW,MAAM,KAAK,QAAQ,CAAC,CAAC;AAE9D,YAAM,KAAK,OAAO;AAClB,UAAI,IAAI;AACN,WAAG;AAAA,UACD;AAAA,YACE,MAAM;AAAA,YACN,SAAS,MAAM,KAAK,cAAc;AAAA,YAClC,gBAAgB,UAAU;AAAA,UAAA;AAAA,UAE5B;AAAA,QAAA;AAAA,MAEJ;AAEA,4BAAA;AAAA,IACF,WAAW,YAAY,QAAQ;AAC7B,YAAM,KAAK,OAAO;AAClB,UAAI,IAAI;AACN,WAAG;AAAA,UACD,EAAE,MAAM,QAAQ,gBAAgB,UAAU,SAAA;AAAA,UAC1C;AAAA,QAAA;AAAA,MAEJ;AAAA,IACF,WAAW,YAAY,eAAe;AACpC,YAAM,SAAS,IAAI,WAAW,OAAO;AACrC,YAAM,aAAa,MAAM,KAAK;AAE9B,YAAM,cAAc,aAAa,kBAAkB;AACnD,QAAE,YAAY,MAAM,QAAQ,WAAW;AAAA,IAEzC,WAAW,YAAY,oBAAoB;AAEzC,6BAAuB;AACvB,2BAAqB,WAAW,IAAI,WAAW,OAAO,GAAG,aAAa;AACtE,6BAAuB;AAAA,IACzB,WAAW,YAAY,UAAU,aAAa;AAC5C,kBAAY,KAAA;AACZ,4BAAA;AAAA,IACF,WAAW,YAAY,UAAU,aAAa;AAC5C,kBAAY,KAAA;AACZ,4BAAA;AAAA,IACF;AAAA,EACF;AAEA,QAAM,eAAe,MAAM;AACzB,0BAAA;AAAA,EACF;AAEA,QAAM,iBAAiB,MAAM;AAC3B,0BAAA;AAAA,EACF;AAEA,QAAM,mBAAmB,MAAM;AAC7B,0BAAA;AAAA,EACF;AAEA,OAAK,GAAG,UAAU,YAAY;AAC9B,YAAU,GAAG,UAAU,iBAAiB;AACxC,SAAO,iBAAiB,WAAW,SAAS;AAC5C,MAAI,aAAa;AACf,gBAAY,GAAG,qBAAqB,YAAY;AAChD,gBAAY,GAAG,iBAAiB,cAAc;AAC9C,gBAAY,GAAG,oBAAoB,gBAAgB;AAAA,EACrD;AAEA,SAAO;AAAA,IACL,IAAI,YAAY;AACd,aAAO;AAAA,IACT;AAAA,IACA,UAAU,IAAgB;AACxB,uBAAiB,IAAI,EAAE;AACvB,aAAO,MAAM,iBAAiB,OAAO,EAAE;AAAA,IACzC;AAAA,IACA,aAAa,IAAgB;AAC3B,0BAAoB,IAAI,EAAE;AAC1B,aAAO,MAAM,oBAAoB,OAAO,EAAE;AAAA,IAC5C;AAAA,IACA,GAAG,OAAiC,IAAgB;AAClD,UAAI,UAAU,WAAW;AACvB,yBAAiB,IAAI,EAAE;AACvB,eAAO,MAAM,iBAAiB,OAAO,EAAE;AAAA,MACzC,OAAO;AACL,4BAAoB,IAAI,EAAE;AAC1B,eAAO,MAAM,oBAAoB,OAAO,EAAE;AAAA,MAC5C;AAAA,IACF;AAAA,IACA,SAAS,MAAM;AACb,mBAAa,KAAK;AAClB,mBAAa,YAAY;AACzB,WAAK,IAAI,UAAU,YAAY;AAC/B,gBAAU,IAAI,UAAU,iBAAiB;AACzC,aAAO,oBAAoB,WAAW,SAAS;AAC/C,UAAI,aAAa;AACf,oBAAY,IAAI,qBAAqB,YAAY;AACjD,oBAAY,IAAI,iBAAiB,cAAc;AAC/C,oBAAY,IAAI,oBAAoB,gBAAgB;AAAA,MACtD;AACA,uBAAiB,MAAA;AACjB,0BAAoB,MAAA;AAAA,IACtB;AAAA,EAAA;AAEJ;"}
|
|
@@ -37,6 +37,12 @@ function serializer$1(obj, spaces = 2) {
|
|
|
37
37
|
spaces
|
|
38
38
|
});
|
|
39
39
|
}
|
|
40
|
+
function getMap(parent, key2) {
|
|
41
|
+
return parent.get(key2);
|
|
42
|
+
}
|
|
43
|
+
function getArray(parent, key2) {
|
|
44
|
+
return parent.get(key2);
|
|
45
|
+
}
|
|
40
46
|
var __defProp = Object.defineProperty;
|
|
41
47
|
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
|
|
42
48
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
@@ -133,8 +139,8 @@ function parse$2(object, doc) {
|
|
|
133
139
|
return mxGraphElement;
|
|
134
140
|
}
|
|
135
141
|
function serialize$1(map) {
|
|
136
|
-
const cells = map
|
|
137
|
-
const cellsOrder = map
|
|
142
|
+
const cells = getMap(map, key$3);
|
|
143
|
+
const cellsOrder = getArray(map, mxCellOrderKey);
|
|
138
144
|
return {
|
|
139
145
|
_attributes: {},
|
|
140
146
|
root: {
|
|
@@ -153,7 +159,7 @@ function parse$1(object) {
|
|
|
153
159
|
return yDiagramElement;
|
|
154
160
|
}
|
|
155
161
|
function serialize(yDiagram) {
|
|
156
|
-
const mxGraphModel = yDiagram
|
|
162
|
+
const mxGraphModel = getMap(yDiagram, key$2);
|
|
157
163
|
return {
|
|
158
164
|
_attributes: {
|
|
159
165
|
name: yDiagram.get("name"),
|
|
@@ -186,10 +192,8 @@ function parse(object, doc) {
|
|
|
186
192
|
return mxfile;
|
|
187
193
|
}
|
|
188
194
|
function serializer(yMxFile) {
|
|
189
|
-
const diagrams = yMxFile
|
|
190
|
-
const diagramOrder = yMxFile
|
|
191
|
-
diagramOrderKey
|
|
192
|
-
);
|
|
195
|
+
const diagrams = getMap(yMxFile, key$1);
|
|
196
|
+
const diagramOrder = getArray(yMxFile, diagramOrderKey);
|
|
193
197
|
const orderIds = diagramOrder ? diagramOrder.toArray() : [];
|
|
194
198
|
const ids = orderIds.length > 0 ? orderIds : diagrams ? Array.from(diagrams.keys()) : [];
|
|
195
199
|
const obj = {
|
|
@@ -210,7 +214,10 @@ function xml2ydoc(xml, doc) {
|
|
|
210
214
|
});
|
|
211
215
|
} else if (mxGraphModel) {
|
|
212
216
|
doc.transact(() => {
|
|
213
|
-
parse$2(
|
|
217
|
+
parse$2(
|
|
218
|
+
mxGraphModel,
|
|
219
|
+
doc
|
|
220
|
+
);
|
|
214
221
|
});
|
|
215
222
|
} else {
|
|
216
223
|
throw new Error("不支持的文件格式");
|
|
@@ -221,18 +228,15 @@ function ydoc2xml(doc, spaces = 0) {
|
|
|
221
228
|
if (doc.share.has(key)) {
|
|
222
229
|
return serializer$1(
|
|
223
230
|
{
|
|
224
|
-
[key]: serializer(
|
|
225
|
-
doc.share.get(key)
|
|
226
|
-
)
|
|
231
|
+
[key]: serializer(doc.getMap(key))
|
|
227
232
|
},
|
|
228
233
|
spaces
|
|
229
234
|
);
|
|
230
|
-
}
|
|
235
|
+
}
|
|
236
|
+
if (doc.share.has(key$2)) {
|
|
231
237
|
return serializer$1(
|
|
232
238
|
{
|
|
233
|
-
[key$2]: serialize$1(
|
|
234
|
-
doc.share.get(key$2)
|
|
235
|
-
)
|
|
239
|
+
[key$2]: serialize$1(doc.getMap(key$2))
|
|
236
240
|
},
|
|
237
241
|
spaces
|
|
238
242
|
);
|
|
@@ -253,4 +257,4 @@ export {
|
|
|
253
257
|
xml2ydoc as x,
|
|
254
258
|
ydoc2xml as y
|
|
255
259
|
};
|
|
256
|
-
//# sourceMappingURL=index-
|
|
260
|
+
//# sourceMappingURL=index-D8PKHH-R.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index-D8PKHH-R.js","sources":["../src/helper/xml.ts","../src/helper/yjs.ts","../src/models/mxCell.ts","../src/models/mxGraphModel.ts","../src/models/diagram.ts","../src/models/mxfile.ts","../src/transform/index.ts"],"sourcesContent":["import { xml2js, js2xml, type ElementCompact } from \"xml-js\";\n\nfunction deepProcess(node: unknown): void {\n if (node == null) return;\n\n if (Array.isArray(node)) {\n for (const item of node) {\n deepProcess(item);\n }\n return;\n }\n\n if (typeof node !== \"object\") return;\n\n const obj = node as Record<string, unknown>;\n const keys = Object.keys(obj);\n for (const key of keys) {\n if (key === \"_attributes\") continue;\n\n let value = obj[key];\n const keyLower = key.toLowerCase();\n\n if (\n (keyLower === \"diagram\" || keyLower === \"mxcell\") &&\n value !== undefined &&\n !Array.isArray(value)\n ) {\n obj[key] = [value];\n value = obj[key];\n }\n\n if (Array.isArray(value)) {\n for (const v of value) deepProcess(v);\n } else if (value && typeof value === \"object\") {\n deepProcess(value);\n }\n }\n}\n\nexport function parse(xml: string) {\n const result = xml2js(xml, { compact: true }) as Record<string, unknown>;\n deepProcess(result);\n return result;\n}\n\nexport function serializer(obj: ElementCompact, spaces = 2) {\n return js2xml(obj, {\n compact: true,\n spaces,\n });\n}\n","import * as Y from \"yjs\";\n\n/**\n * 从 Y.Map 中安全获取子 Map(格式固定,转型安全)\n */\nexport function getMap<T = unknown>(\n parent: Y.Map<unknown>,\n key: string,\n): Y.Map<T> | undefined {\n return parent.get(key) as Y.Map<T> | undefined;\n}\n\n/**\n * 从 Y.Map 中安全获取子 Array(格式固定,转型安全)\n */\nexport function getArray<T = unknown>(\n parent: Y.Map<unknown>,\n key: string,\n): Y.Array<T> | undefined {\n return parent.get(key) as Y.Array<T> | undefined;\n}\n","import * as Y from \"yjs\";\nimport { xml2js, js2xml } from \"xml-js\";\nimport type { ElementCompact } from \"xml-js\";\n\nexport const key = \"mxCell\";\n\nconst mxGeometryKey = \"mxGeometry\";\nconst mxGeometryAttributeKey = \"geometry\";\n\nexport interface MxCellModel extends ElementCompact {\n [mxGeometryKey]?: ElementCompact;\n}\n\nexport function parse(object: MxCellModel): Y.XmlElement {\n const xmlElement = new Y.XmlElement(\"mxCell\");\n\n for (const attribute of Object.keys(object._attributes || {})) {\n xmlElement.setAttribute(\n attribute,\n `${object._attributes?.[attribute] || \"\"}`\n );\n }\n\n if (object[mxGeometryKey]) {\n const geometry = object[mxGeometryKey];\n const geometryString = js2xml(geometry, {\n compact: true,\n });\n xmlElement.setAttribute(mxGeometryAttributeKey, geometryString);\n delete object[mxGeometryKey];\n }\n\n return xmlElement;\n}\n\nexport function serialize(xmlElement: Y.XmlElement) {\n const rawAttributes = {\n ...xmlElement.getAttributes(),\n };\n\n // 提取 mxGeometry(不需要转义,它本身就是 XML 字符串)\n let mxGeometry: ElementCompact | null = null;\n let mxGeometryString: string | undefined;\n\n if (mxGeometryAttributeKey in rawAttributes) {\n mxGeometryString = rawAttributes[mxGeometryAttributeKey];\n delete rawAttributes[mxGeometryAttributeKey];\n }\n\n // 转义其他属性值中的特殊字符\n const attributes: Record<string, string> = {};\n for (const [key, value] of Object.entries(rawAttributes)) {\n if (typeof value === 'string') {\n attributes[key] = value\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/\"/g, '"')\n .replace(/'/g, ''');\n } else if (value != null) {\n attributes[key] = String(value);\n }\n }\n\n // 解析 mxGeometry\n if (mxGeometryString) {\n try {\n const parsed = xml2js(mxGeometryString, { compact: true }) as Record<string, ElementCompact>;\n mxGeometry = parsed[mxGeometryKey] ?? null;\n if (mxGeometry && mxGeometry._attributes) {\n mxGeometry._attributes[\"as\"] = \"geometry\";\n }\n } catch (e) {\n console.warn(\"[y-mxgraph] Failed to parse mxGeometry:\", e);\n }\n }\n\n const obj: Record<string, unknown> = {\n _attributes: attributes,\n };\n\n if (mxGeometry) {\n obj[mxGeometryKey] = mxGeometry;\n }\n\n return obj;\n}\n","import * as Y from \"yjs\";\nimport { getMap, getArray } from \"../helper/yjs\";\n\nimport {\n key as mxCellKey,\n parse as parseMxCell,\n serialize as serializeMxCell,\n type MxCellModel,\n} from \"./mxCell\";\nimport type { ElementCompact } from \"xml-js\";\n\nexport const key = \"mxGraphModel\";\nexport const mxCellOrderKey = mxCellKey + \"Order\";\n\nexport interface MxGraphModel extends ElementCompact {\n root: {\n mxCell: MxCellModel[];\n };\n}\n\nexport type YMxGraphModel = Y.Map<unknown>;\n\nexport function parse(object: MxGraphModel, doc?: Y.Doc) {\n const mxCells = (object.root[mxCellKey] || []).map((cell: MxCellModel) => {\n return {\n value: parseMxCell(cell),\n id: (cell._attributes?.id || \"\") as string,\n };\n });\n\n const mxGraphElement = doc?.getMap(key) || new Y.Map();\n\n const cells = new Y.Map<Y.XmlElement>();\n const cellsOrder = new Y.Array<string>();\n\n mxCells.forEach((cell) => {\n cells.set(cell.id, cell.value);\n });\n\n cellsOrder.push(mxCells.map((cell) => cell.id));\n\n mxGraphElement.set(mxCellKey, cells);\n mxGraphElement.set(mxCellOrderKey, cellsOrder);\n\n return mxGraphElement as YMxGraphModel;\n}\n\nexport function serialize(map: YMxGraphModel) {\n const cells = getMap<Y.XmlElement>(map, mxCellKey)!;\n const cellsOrder = getArray<string>(map, mxCellOrderKey)!;\n return {\n _attributes: {},\n root: {\n [mxCellKey]: cellsOrder\n .toArray()\n .map((id) => serializeMxCell(cells!.get(id) as Y.XmlElement)),\n },\n };\n}\n","import * as Y from \"yjs\";\nimport { getMap } from \"../helper/yjs\";\nimport {\n parse as parseMxGraphModel,\n serialize as serializeMxGraphModel,\n key as mxGraphModelKey,\n type MxGraphModel,\n type YMxGraphModel,\n} from \"./mxGraphModel\";\nimport type { ElementCompact } from \"xml-js\";\n\nexport const key = \"diagram\";\n\nexport interface Diagram extends ElementCompact {\n mxGraphModel: MxGraphModel;\n}\n\nexport type YDiagram = Y.Map<unknown>;\n\nexport function parse(object: Diagram): YDiagram {\n const yDiagramElement = new Y.Map();\n yDiagramElement.set(\"name\", `${object._attributes?.name || \"\"}`);\n yDiagramElement.set(\"id\", `${object._attributes?.id || \"\"}`);\n\n const mxGraphModel = parseMxGraphModel(object[mxGraphModelKey]);\n\n yDiagramElement.set(mxGraphModelKey, mxGraphModel);\n return yDiagramElement as YDiagram;\n}\n\nexport function serialize(yDiagram: YDiagram) {\n const mxGraphModel = getMap(yDiagram, mxGraphModelKey);\n\n return {\n _attributes: {\n name: yDiagram.get(\"name\") as string,\n id: yDiagram.get(\"id\") as string,\n },\n [mxGraphModelKey]: mxGraphModel\n ? serializeMxGraphModel(mxGraphModel)\n : undefined,\n };\n}\n","import * as Y from \"yjs\";\nimport { getMap, getArray } from \"../helper/yjs\";\nimport {\n parse as parseDiagram,\n key as diagramKey,\n serialize as serializeDiagram,\n} from \"./diagram\";\nimport type { Diagram, YDiagram } from \"./diagram\";\nimport type { ElementCompact } from \"xml-js\";\n\nexport const key = \"mxfile\";\nexport const diagramOrderKey = diagramKey + \"Order\";\n\nexport type YMxFile = Y.Map<unknown>;\n\nexport interface MxFile extends ElementCompact {\n diagram: Diagram[];\n}\n\nexport function parse(object: MxFile, doc: Y.Doc) {\n const mxfile = doc.getMap(key);\n mxfile.set(\"pages\", (object._attributes?.pages || \"1\") + \"\");\n\n const diagramList = object.diagram.map((diagram) => ({\n value: parseDiagram(diagram),\n id: (diagram._attributes?.id || \"\") as string,\n }));\n const diagramMap = new Y.Map<YDiagram>();\n const diagramOrder = new Y.Array<string>();\n diagramList.forEach((diagram) => {\n diagramMap.set(diagram.id, diagram.value);\n });\n diagramOrder.push(diagramList.map((diagram) => diagram.id));\n\n mxfile.set(diagramKey, diagramMap);\n mxfile.set(diagramOrderKey, diagramOrder);\n return mxfile;\n}\n\nexport function serializer(yMxFile: YMxFile): ElementCompact {\n const diagrams = getMap<YDiagram>(yMxFile, diagramKey);\n const diagramOrder = getArray<string>(yMxFile, diagramOrderKey);\n\n const orderIds = diagramOrder ? diagramOrder.toArray() : [];\n // 如果 diagramOrder 为空但 diagram map 不为空,使用 diagram map 中的所有 ID\n const ids =\n orderIds.length > 0\n ? orderIds\n : diagrams\n ? Array.from(diagrams.keys())\n : [];\n\n const obj: Record<string, unknown> = {\n _attributes: {\n pages: (yMxFile.get(\"pages\") as string) || \"1\",\n },\n [diagramKey]: ids\n .map((id) => diagrams!.get(id) as YDiagram)\n .filter((d): d is YDiagram => !!d)\n .map((diagramElement) => serializeDiagram(diagramElement)),\n };\n\n return obj as ElementCompact;\n}\n","import * as Y from \"yjs\";\nimport { parse, serializer } from \"../helper/xml\";\nimport {\n parse as parseMxFile,\n key as mxfileKey,\n serializer as serializerMxFile,\n type YMxFile,\n} from \"../models/mxfile\";\nimport {\n parse as parseMxGraphModel,\n key as mxGraphModelKey,\n serialize as serializerMxGraphModel,\n type YMxGraphModel,\n} from \"../models/mxGraphModel\";\n\nexport function xml2ydoc(xml: string, doc: Y.Doc): Y.Doc {\n const object = parse(xml);\n\n const mxfile = (object as Record<string, unknown>).mxfile;\n const mxGraphModel = (object as Record<string, unknown>).mxGraphModel;\n if (mxfile) {\n doc.transact(() => {\n parseMxFile(mxfile as import(\"../models/mxfile\").MxFile, doc);\n });\n } else if (mxGraphModel) {\n doc.transact(() => {\n parseMxGraphModel(\n mxGraphModel as import(\"../models/mxGraphModel\").MxGraphModel,\n doc,\n );\n });\n } else {\n throw new Error(\"不支持的文件格式\");\n }\n\n return doc;\n}\n\nexport function ydoc2xml(doc: Y.Doc, spaces = 0): string {\n if (doc.share.has(mxfileKey)) {\n return serializer(\n {\n [mxfileKey]: serializerMxFile(doc.getMap(mxfileKey)),\n },\n spaces,\n );\n }\n if (doc.share.has(mxGraphModelKey)) {\n return serializer(\n {\n [mxGraphModelKey]: serializerMxGraphModel(doc.getMap(mxGraphModelKey)),\n },\n spaces,\n );\n }\n\n return \"\";\n}\n"],"names":["key","parse","serializer","serialize","mxCellKey","parseMxCell","serializeMxCell","parseMxGraphModel","mxGraphModelKey","serializeMxGraphModel","diagramKey","_a","parseDiagram","serializeDiagram","parseMxFile","mxfileKey","serializerMxFile","serializerMxGraphModel"],"mappings":"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"}
|
|
@@ -55,6 +55,12 @@ function serializer$1(obj, spaces = 2) {
|
|
|
55
55
|
spaces
|
|
56
56
|
});
|
|
57
57
|
}
|
|
58
|
+
function getMap(parent, key2) {
|
|
59
|
+
return parent.get(key2);
|
|
60
|
+
}
|
|
61
|
+
function getArray(parent, key2) {
|
|
62
|
+
return parent.get(key2);
|
|
63
|
+
}
|
|
58
64
|
var __defProp = Object.defineProperty;
|
|
59
65
|
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
|
|
60
66
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
@@ -151,8 +157,8 @@ function parse$2(object, doc) {
|
|
|
151
157
|
return mxGraphElement;
|
|
152
158
|
}
|
|
153
159
|
function serialize$1(map) {
|
|
154
|
-
const cells = map
|
|
155
|
-
const cellsOrder = map
|
|
160
|
+
const cells = getMap(map, key$3);
|
|
161
|
+
const cellsOrder = getArray(map, mxCellOrderKey);
|
|
156
162
|
return {
|
|
157
163
|
_attributes: {},
|
|
158
164
|
root: {
|
|
@@ -171,7 +177,7 @@ function parse$1(object) {
|
|
|
171
177
|
return yDiagramElement;
|
|
172
178
|
}
|
|
173
179
|
function serialize(yDiagram) {
|
|
174
|
-
const mxGraphModel = yDiagram
|
|
180
|
+
const mxGraphModel = getMap(yDiagram, key$2);
|
|
175
181
|
return {
|
|
176
182
|
_attributes: {
|
|
177
183
|
name: yDiagram.get("name"),
|
|
@@ -204,10 +210,8 @@ function parse(object, doc) {
|
|
|
204
210
|
return mxfile;
|
|
205
211
|
}
|
|
206
212
|
function serializer(yMxFile) {
|
|
207
|
-
const diagrams = yMxFile
|
|
208
|
-
const diagramOrder = yMxFile
|
|
209
|
-
diagramOrderKey
|
|
210
|
-
);
|
|
213
|
+
const diagrams = getMap(yMxFile, key$1);
|
|
214
|
+
const diagramOrder = getArray(yMxFile, diagramOrderKey);
|
|
211
215
|
const orderIds = diagramOrder ? diagramOrder.toArray() : [];
|
|
212
216
|
const ids = orderIds.length > 0 ? orderIds : diagrams ? Array.from(diagrams.keys()) : [];
|
|
213
217
|
const obj = {
|
|
@@ -228,7 +232,10 @@ function xml2ydoc(xml, doc) {
|
|
|
228
232
|
});
|
|
229
233
|
} else if (mxGraphModel) {
|
|
230
234
|
doc.transact(() => {
|
|
231
|
-
parse$2(
|
|
235
|
+
parse$2(
|
|
236
|
+
mxGraphModel,
|
|
237
|
+
doc
|
|
238
|
+
);
|
|
232
239
|
});
|
|
233
240
|
} else {
|
|
234
241
|
throw new Error("不支持的文件格式");
|
|
@@ -239,18 +246,15 @@ function ydoc2xml(doc, spaces = 0) {
|
|
|
239
246
|
if (doc.share.has(key)) {
|
|
240
247
|
return serializer$1(
|
|
241
248
|
{
|
|
242
|
-
[key]: serializer(
|
|
243
|
-
doc.share.get(key)
|
|
244
|
-
)
|
|
249
|
+
[key]: serializer(doc.getMap(key))
|
|
245
250
|
},
|
|
246
251
|
spaces
|
|
247
252
|
);
|
|
248
|
-
}
|
|
253
|
+
}
|
|
254
|
+
if (doc.share.has(key$2)) {
|
|
249
255
|
return serializer$1(
|
|
250
256
|
{
|
|
251
|
-
[key$2]: serialize$1(
|
|
252
|
-
doc.share.get(key$2)
|
|
253
|
-
)
|
|
257
|
+
[key$2]: serialize$1(doc.getMap(key$2))
|
|
254
258
|
},
|
|
255
259
|
spaces
|
|
256
260
|
);
|
|
@@ -269,4 +273,4 @@ exports.serialize = serialize;
|
|
|
269
273
|
exports.serializer = serializer$1;
|
|
270
274
|
exports.xml2ydoc = xml2ydoc;
|
|
271
275
|
exports.ydoc2xml = ydoc2xml;
|
|
272
|
-
//# sourceMappingURL=index-
|
|
276
|
+
//# sourceMappingURL=index-DkYwhpfJ.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index-DkYwhpfJ.cjs","sources":["../src/helper/xml.ts","../src/helper/yjs.ts","../src/models/mxCell.ts","../src/models/mxGraphModel.ts","../src/models/diagram.ts","../src/models/mxfile.ts","../src/transform/index.ts"],"sourcesContent":["import { xml2js, js2xml, type ElementCompact } from \"xml-js\";\n\nfunction deepProcess(node: unknown): void {\n if (node == null) return;\n\n if (Array.isArray(node)) {\n for (const item of node) {\n deepProcess(item);\n }\n return;\n }\n\n if (typeof node !== \"object\") return;\n\n const obj = node as Record<string, unknown>;\n const keys = Object.keys(obj);\n for (const key of keys) {\n if (key === \"_attributes\") continue;\n\n let value = obj[key];\n const keyLower = key.toLowerCase();\n\n if (\n (keyLower === \"diagram\" || keyLower === \"mxcell\") &&\n value !== undefined &&\n !Array.isArray(value)\n ) {\n obj[key] = [value];\n value = obj[key];\n }\n\n if (Array.isArray(value)) {\n for (const v of value) deepProcess(v);\n } else if (value && typeof value === \"object\") {\n deepProcess(value);\n }\n }\n}\n\nexport function parse(xml: string) {\n const result = xml2js(xml, { compact: true }) as Record<string, unknown>;\n deepProcess(result);\n return result;\n}\n\nexport function serializer(obj: ElementCompact, spaces = 2) {\n return js2xml(obj, {\n compact: true,\n spaces,\n });\n}\n","import * as Y from \"yjs\";\n\n/**\n * 从 Y.Map 中安全获取子 Map(格式固定,转型安全)\n */\nexport function getMap<T = unknown>(\n parent: Y.Map<unknown>,\n key: string,\n): Y.Map<T> | undefined {\n return parent.get(key) as Y.Map<T> | undefined;\n}\n\n/**\n * 从 Y.Map 中安全获取子 Array(格式固定,转型安全)\n */\nexport function getArray<T = unknown>(\n parent: Y.Map<unknown>,\n key: string,\n): Y.Array<T> | undefined {\n return parent.get(key) as Y.Array<T> | undefined;\n}\n","import * as Y from \"yjs\";\nimport { xml2js, js2xml } from \"xml-js\";\nimport type { ElementCompact } from \"xml-js\";\n\nexport const key = \"mxCell\";\n\nconst mxGeometryKey = \"mxGeometry\";\nconst mxGeometryAttributeKey = \"geometry\";\n\nexport interface MxCellModel extends ElementCompact {\n [mxGeometryKey]?: ElementCompact;\n}\n\nexport function parse(object: MxCellModel): Y.XmlElement {\n const xmlElement = new Y.XmlElement(\"mxCell\");\n\n for (const attribute of Object.keys(object._attributes || {})) {\n xmlElement.setAttribute(\n attribute,\n `${object._attributes?.[attribute] || \"\"}`\n );\n }\n\n if (object[mxGeometryKey]) {\n const geometry = object[mxGeometryKey];\n const geometryString = js2xml(geometry, {\n compact: true,\n });\n xmlElement.setAttribute(mxGeometryAttributeKey, geometryString);\n delete object[mxGeometryKey];\n }\n\n return xmlElement;\n}\n\nexport function serialize(xmlElement: Y.XmlElement) {\n const rawAttributes = {\n ...xmlElement.getAttributes(),\n };\n\n // 提取 mxGeometry(不需要转义,它本身就是 XML 字符串)\n let mxGeometry: ElementCompact | null = null;\n let mxGeometryString: string | undefined;\n\n if (mxGeometryAttributeKey in rawAttributes) {\n mxGeometryString = rawAttributes[mxGeometryAttributeKey];\n delete rawAttributes[mxGeometryAttributeKey];\n }\n\n // 转义其他属性值中的特殊字符\n const attributes: Record<string, string> = {};\n for (const [key, value] of Object.entries(rawAttributes)) {\n if (typeof value === 'string') {\n attributes[key] = value\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/\"/g, '"')\n .replace(/'/g, ''');\n } else if (value != null) {\n attributes[key] = String(value);\n }\n }\n\n // 解析 mxGeometry\n if (mxGeometryString) {\n try {\n const parsed = xml2js(mxGeometryString, { compact: true }) as Record<string, ElementCompact>;\n mxGeometry = parsed[mxGeometryKey] ?? null;\n if (mxGeometry && mxGeometry._attributes) {\n mxGeometry._attributes[\"as\"] = \"geometry\";\n }\n } catch (e) {\n console.warn(\"[y-mxgraph] Failed to parse mxGeometry:\", e);\n }\n }\n\n const obj: Record<string, unknown> = {\n _attributes: attributes,\n };\n\n if (mxGeometry) {\n obj[mxGeometryKey] = mxGeometry;\n }\n\n return obj;\n}\n","import * as Y from \"yjs\";\nimport { getMap, getArray } from \"../helper/yjs\";\n\nimport {\n key as mxCellKey,\n parse as parseMxCell,\n serialize as serializeMxCell,\n type MxCellModel,\n} from \"./mxCell\";\nimport type { ElementCompact } from \"xml-js\";\n\nexport const key = \"mxGraphModel\";\nexport const mxCellOrderKey = mxCellKey + \"Order\";\n\nexport interface MxGraphModel extends ElementCompact {\n root: {\n mxCell: MxCellModel[];\n };\n}\n\nexport type YMxGraphModel = Y.Map<unknown>;\n\nexport function parse(object: MxGraphModel, doc?: Y.Doc) {\n const mxCells = (object.root[mxCellKey] || []).map((cell: MxCellModel) => {\n return {\n value: parseMxCell(cell),\n id: (cell._attributes?.id || \"\") as string,\n };\n });\n\n const mxGraphElement = doc?.getMap(key) || new Y.Map();\n\n const cells = new Y.Map<Y.XmlElement>();\n const cellsOrder = new Y.Array<string>();\n\n mxCells.forEach((cell) => {\n cells.set(cell.id, cell.value);\n });\n\n cellsOrder.push(mxCells.map((cell) => cell.id));\n\n mxGraphElement.set(mxCellKey, cells);\n mxGraphElement.set(mxCellOrderKey, cellsOrder);\n\n return mxGraphElement as YMxGraphModel;\n}\n\nexport function serialize(map: YMxGraphModel) {\n const cells = getMap<Y.XmlElement>(map, mxCellKey)!;\n const cellsOrder = getArray<string>(map, mxCellOrderKey)!;\n return {\n _attributes: {},\n root: {\n [mxCellKey]: cellsOrder\n .toArray()\n .map((id) => serializeMxCell(cells!.get(id) as Y.XmlElement)),\n },\n };\n}\n","import * as Y from \"yjs\";\nimport { getMap } from \"../helper/yjs\";\nimport {\n parse as parseMxGraphModel,\n serialize as serializeMxGraphModel,\n key as mxGraphModelKey,\n type MxGraphModel,\n type YMxGraphModel,\n} from \"./mxGraphModel\";\nimport type { ElementCompact } from \"xml-js\";\n\nexport const key = \"diagram\";\n\nexport interface Diagram extends ElementCompact {\n mxGraphModel: MxGraphModel;\n}\n\nexport type YDiagram = Y.Map<unknown>;\n\nexport function parse(object: Diagram): YDiagram {\n const yDiagramElement = new Y.Map();\n yDiagramElement.set(\"name\", `${object._attributes?.name || \"\"}`);\n yDiagramElement.set(\"id\", `${object._attributes?.id || \"\"}`);\n\n const mxGraphModel = parseMxGraphModel(object[mxGraphModelKey]);\n\n yDiagramElement.set(mxGraphModelKey, mxGraphModel);\n return yDiagramElement as YDiagram;\n}\n\nexport function serialize(yDiagram: YDiagram) {\n const mxGraphModel = getMap(yDiagram, mxGraphModelKey);\n\n return {\n _attributes: {\n name: yDiagram.get(\"name\") as string,\n id: yDiagram.get(\"id\") as string,\n },\n [mxGraphModelKey]: mxGraphModel\n ? serializeMxGraphModel(mxGraphModel)\n : undefined,\n };\n}\n","import * as Y from \"yjs\";\nimport { getMap, getArray } from \"../helper/yjs\";\nimport {\n parse as parseDiagram,\n key as diagramKey,\n serialize as serializeDiagram,\n} from \"./diagram\";\nimport type { Diagram, YDiagram } from \"./diagram\";\nimport type { ElementCompact } from \"xml-js\";\n\nexport const key = \"mxfile\";\nexport const diagramOrderKey = diagramKey + \"Order\";\n\nexport type YMxFile = Y.Map<unknown>;\n\nexport interface MxFile extends ElementCompact {\n diagram: Diagram[];\n}\n\nexport function parse(object: MxFile, doc: Y.Doc) {\n const mxfile = doc.getMap(key);\n mxfile.set(\"pages\", (object._attributes?.pages || \"1\") + \"\");\n\n const diagramList = object.diagram.map((diagram) => ({\n value: parseDiagram(diagram),\n id: (diagram._attributes?.id || \"\") as string,\n }));\n const diagramMap = new Y.Map<YDiagram>();\n const diagramOrder = new Y.Array<string>();\n diagramList.forEach((diagram) => {\n diagramMap.set(diagram.id, diagram.value);\n });\n diagramOrder.push(diagramList.map((diagram) => diagram.id));\n\n mxfile.set(diagramKey, diagramMap);\n mxfile.set(diagramOrderKey, diagramOrder);\n return mxfile;\n}\n\nexport function serializer(yMxFile: YMxFile): ElementCompact {\n const diagrams = getMap<YDiagram>(yMxFile, diagramKey);\n const diagramOrder = getArray<string>(yMxFile, diagramOrderKey);\n\n const orderIds = diagramOrder ? diagramOrder.toArray() : [];\n // 如果 diagramOrder 为空但 diagram map 不为空,使用 diagram map 中的所有 ID\n const ids =\n orderIds.length > 0\n ? orderIds\n : diagrams\n ? Array.from(diagrams.keys())\n : [];\n\n const obj: Record<string, unknown> = {\n _attributes: {\n pages: (yMxFile.get(\"pages\") as string) || \"1\",\n },\n [diagramKey]: ids\n .map((id) => diagrams!.get(id) as YDiagram)\n .filter((d): d is YDiagram => !!d)\n .map((diagramElement) => serializeDiagram(diagramElement)),\n };\n\n return obj as ElementCompact;\n}\n","import * as Y from \"yjs\";\nimport { parse, serializer } from \"../helper/xml\";\nimport {\n parse as parseMxFile,\n key as mxfileKey,\n serializer as serializerMxFile,\n type YMxFile,\n} from \"../models/mxfile\";\nimport {\n parse as parseMxGraphModel,\n key as mxGraphModelKey,\n serialize as serializerMxGraphModel,\n type YMxGraphModel,\n} from \"../models/mxGraphModel\";\n\nexport function xml2ydoc(xml: string, doc: Y.Doc): Y.Doc {\n const object = parse(xml);\n\n const mxfile = (object as Record<string, unknown>).mxfile;\n const mxGraphModel = (object as Record<string, unknown>).mxGraphModel;\n if (mxfile) {\n doc.transact(() => {\n parseMxFile(mxfile as import(\"../models/mxfile\").MxFile, doc);\n });\n } else if (mxGraphModel) {\n doc.transact(() => {\n parseMxGraphModel(\n mxGraphModel as import(\"../models/mxGraphModel\").MxGraphModel,\n doc,\n );\n });\n } else {\n throw new Error(\"不支持的文件格式\");\n }\n\n return doc;\n}\n\nexport function ydoc2xml(doc: Y.Doc, spaces = 0): string {\n if (doc.share.has(mxfileKey)) {\n return serializer(\n {\n [mxfileKey]: serializerMxFile(doc.getMap(mxfileKey)),\n },\n spaces,\n );\n }\n if (doc.share.has(mxGraphModelKey)) {\n return serializer(\n {\n [mxGraphModelKey]: serializerMxGraphModel(doc.getMap(mxGraphModelKey)),\n },\n spaces,\n );\n }\n\n return \"\";\n}\n"],"names":["key","parse","xml2js","serializer","js2xml","Y","serialize","mxCellKey","parseMxCell","serializeMxCell","parseMxGraphModel","mxGraphModelKey","serializeMxGraphModel","diagramKey","_a","parseDiagram","serializeDiagram","parseMxFile","mxfileKey","serializerMxFile","serializerMxGraphModel"],"mappings":"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"}
|
package/models/diagram.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"diagram.d.ts","sourceRoot":"","sources":["../../src/models/diagram.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,KAAK,CAAC;
|
|
1
|
+
{"version":3,"file":"diagram.d.ts","sourceRoot":"","sources":["../../src/models/diagram.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,KAAK,CAAC;AAEzB,OAAO,EAIL,KAAK,YAAY,EAElB,MAAM,gBAAgB,CAAC;AACxB,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,QAAQ,CAAC;AAE7C,eAAO,MAAM,GAAG,YAAY,CAAC;AAE7B,MAAM,WAAW,OAAQ,SAAQ,cAAc;IAC7C,YAAY,EAAE,YAAY,CAAC;CAC5B;AAED,MAAM,MAAM,QAAQ,GAAG,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;AAEtC,wBAAgB,KAAK,CAAC,MAAM,EAAE,OAAO,GAAG,QAAQ,CAS/C;AAED,wBAAgB,SAAS,CAAC,QAAQ,EAAE,QAAQ;;cAKR,MAAM;YACV,MAAM;;;;;;;;EAMrC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mxGraphModel.d.ts","sourceRoot":"","sources":["../../src/models/mxGraphModel.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,KAAK,CAAC;
|
|
1
|
+
{"version":3,"file":"mxGraphModel.d.ts","sourceRoot":"","sources":["../../src/models/mxGraphModel.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,KAAK,CAAC;AAGzB,OAAO,EAIL,KAAK,WAAW,EACjB,MAAM,UAAU,CAAC;AAClB,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,QAAQ,CAAC;AAE7C,eAAO,MAAM,GAAG,iBAAiB,CAAC;AAClC,eAAO,MAAM,cAAc,QAAsB,CAAC;AAElD,MAAM,WAAW,YAAa,SAAQ,cAAc;IAClD,IAAI,EAAE;QACJ,MAAM,EAAE,WAAW,EAAE,CAAC;KACvB,CAAC;CACH;AAED,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;AAE3C,wBAAgB,KAAK,CAAC,MAAM,EAAE,YAAY,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC,GAAG,GAsB5B,aAAa,CACvC;AAED,wBAAgB,SAAS,CAAC,GAAG,EAAE,aAAa;;;;;EAW3C"}
|
package/models/mxfile.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mxfile.d.ts","sourceRoot":"","sources":["../../src/models/mxfile.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,KAAK,CAAC;
|
|
1
|
+
{"version":3,"file":"mxfile.d.ts","sourceRoot":"","sources":["../../src/models/mxfile.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,KAAK,CAAC;AAOzB,OAAO,KAAK,EAAE,OAAO,EAAY,MAAM,WAAW,CAAC;AACnD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,QAAQ,CAAC;AAE7C,eAAO,MAAM,GAAG,WAAW,CAAC;AAC5B,eAAO,MAAM,eAAe,QAAuB,CAAC;AAEpD,MAAM,MAAM,OAAO,GAAG,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;AAErC,MAAM,WAAW,MAAO,SAAQ,cAAc;IAC5C,OAAO,EAAE,OAAO,EAAE,CAAC;CACpB;AAED,wBAAgB,KAAK,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC,GAAG,kBAkB/C;AAED,wBAAgB,UAAU,CAAC,OAAO,EAAE,OAAO,GAAG,cAAc,CAwB3D"}
|
package/package.json
CHANGED
package/transform/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/transform/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,KAAK,CAAC;AAezB,wBAAgB,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,GAAG,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/transform/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,KAAK,CAAC;AAezB,wBAAgB,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,GAAG,CAqBvD;AAED,wBAAgB,QAAQ,CAAC,GAAG,EAAE,CAAC,CAAC,GAAG,EAAE,MAAM,SAAI,GAAG,MAAM,CAmBvD"}
|
package/transform.cjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
-
const transform = require("./index-
|
|
3
|
+
const transform = require("./index-DkYwhpfJ.cjs");
|
|
4
4
|
exports.xml2ydoc = transform.xml2ydoc;
|
|
5
5
|
exports.ydoc2xml = transform.ydoc2xml;
|
|
6
6
|
//# sourceMappingURL=transform.cjs.map
|
package/transform.js
CHANGED
package/y-mxgraph.cjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
-
const transform = require("./index-
|
|
3
|
+
const transform = require("./index-DkYwhpfJ.cjs");
|
|
4
4
|
const Y = require("yjs");
|
|
5
5
|
const colord = require("colord");
|
|
6
6
|
function _interopNamespaceDefault(e) {
|
package/y-mxgraph.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { k as key, a as key$1, d as diagramOrderKey, b as key$2, m as mxCellOrderKey, c as key$3, p as parse, e as parse$1, f as serializer, s as serialize, x as xml2ydoc, y as ydoc2xml } from "./index-
|
|
1
|
+
import { k as key, a as key$1, d as diagramOrderKey, b as key$2, m as mxCellOrderKey, c as key$3, p as parse, e as parse$1, f as serializer, s as serialize, x as xml2ydoc, y as ydoc2xml } from "./index-D8PKHH-R.js";
|
|
2
2
|
import * as Y from "yjs";
|
|
3
3
|
import { colord } from "colord";
|
|
4
4
|
var __defProp$1 = Object.defineProperty;
|
package/index-CcOHH2TY.cjs.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index-CcOHH2TY.cjs","sources":["../src/helper/xml.ts","../src/models/mxCell.ts","../src/models/mxGraphModel.ts","../src/models/diagram.ts","../src/models/mxfile.ts","../src/transform/index.ts"],"sourcesContent":["import { xml2js, js2xml, type ElementCompact } from \"xml-js\";\n\nfunction deepProcess(node: unknown): void {\n if (node == null) return;\n\n if (Array.isArray(node)) {\n for (const item of node) {\n deepProcess(item);\n }\n return;\n }\n\n if (typeof node !== \"object\") return;\n\n const obj = node as Record<string, unknown>;\n const keys = Object.keys(obj);\n for (const key of keys) {\n if (key === \"_attributes\") continue;\n\n let value = obj[key];\n const keyLower = key.toLowerCase();\n\n if (\n (keyLower === \"diagram\" || keyLower === \"mxcell\") &&\n value !== undefined &&\n !Array.isArray(value)\n ) {\n obj[key] = [value];\n value = obj[key];\n }\n\n if (Array.isArray(value)) {\n for (const v of value) deepProcess(v);\n } else if (value && typeof value === \"object\") {\n deepProcess(value);\n }\n }\n}\n\nexport function parse(xml: string) {\n const result = xml2js(xml, { compact: true }) as Record<string, unknown>;\n deepProcess(result);\n return result;\n}\n\nexport function serializer(obj: ElementCompact, spaces = 2) {\n return js2xml(obj, {\n compact: true,\n spaces,\n });\n}\n","import * as Y from \"yjs\";\nimport { xml2js, js2xml } from \"xml-js\";\nimport type { ElementCompact } from \"xml-js\";\n\nexport const key = \"mxCell\";\n\nconst mxGeometryKey = \"mxGeometry\";\nconst mxGeometryAttributeKey = \"geometry\";\n\nexport interface MxCellModel extends ElementCompact {\n [mxGeometryKey]?: ElementCompact;\n}\n\nexport function parse(object: MxCellModel): Y.XmlElement {\n const xmlElement = new Y.XmlElement(\"mxCell\");\n\n for (const attribute of Object.keys(object._attributes || {})) {\n xmlElement.setAttribute(\n attribute,\n `${object._attributes?.[attribute] || \"\"}`\n );\n }\n\n if (object[mxGeometryKey]) {\n const geometry = object[mxGeometryKey];\n const geometryString = js2xml(geometry, {\n compact: true,\n });\n xmlElement.setAttribute(mxGeometryAttributeKey, geometryString);\n delete object[mxGeometryKey];\n }\n\n return xmlElement;\n}\n\nexport function serialize(xmlElement: Y.XmlElement) {\n const rawAttributes = {\n ...xmlElement.getAttributes(),\n };\n\n // 提取 mxGeometry(不需要转义,它本身就是 XML 字符串)\n let mxGeometry: ElementCompact | null = null;\n let mxGeometryString: string | undefined;\n\n if (mxGeometryAttributeKey in rawAttributes) {\n mxGeometryString = rawAttributes[mxGeometryAttributeKey];\n delete rawAttributes[mxGeometryAttributeKey];\n }\n\n // 转义其他属性值中的特殊字符\n const attributes: Record<string, string> = {};\n for (const [key, value] of Object.entries(rawAttributes)) {\n if (typeof value === 'string') {\n attributes[key] = value\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/\"/g, '"')\n .replace(/'/g, ''');\n } else if (value != null) {\n attributes[key] = String(value);\n }\n }\n\n // 解析 mxGeometry\n if (mxGeometryString) {\n try {\n const parsed = xml2js(mxGeometryString, { compact: true }) as Record<string, ElementCompact>;\n mxGeometry = parsed[mxGeometryKey] ?? null;\n if (mxGeometry && mxGeometry._attributes) {\n mxGeometry._attributes[\"as\"] = \"geometry\";\n }\n } catch (e) {\n console.warn(\"[y-mxgraph] Failed to parse mxGeometry:\", e);\n }\n }\n\n const obj: Record<string, unknown> = {\n _attributes: attributes,\n };\n\n if (mxGeometry) {\n obj[mxGeometryKey] = mxGeometry;\n }\n\n return obj;\n}\n","import * as Y from \"yjs\";\n\nimport {\n key as mxCellKey,\n parse as parseMxCell,\n serialize as serializeMxCell,\n type MxCellModel,\n} from \"./mxCell\";\nimport type { ElementCompact } from \"xml-js\";\n\nexport const key = \"mxGraphModel\";\nexport const mxCellOrderKey = mxCellKey + \"Order\";\n\nexport interface MxGraphModel extends ElementCompact {\n root: {\n mxCell: MxCellModel[];\n };\n}\n\nexport type YMxGraphModel = Y.Map<unknown>;\n\nexport function parse(object: MxGraphModel, doc?: Y.Doc) {\n const mxCells = (object.root[mxCellKey] || []).map((cell: MxCellModel) => {\n return {\n value: parseMxCell(cell),\n id: (cell._attributes?.id || \"\") as string,\n };\n });\n\n const mxGraphElement = doc?.getMap(key) || new Y.Map();\n\n const cells = new Y.Map<Y.XmlElement>();\n const cellsOrder = new Y.Array<string>();\n\n mxCells.forEach((cell) => {\n cells.set(cell.id, cell.value);\n });\n\n cellsOrder.push(mxCells.map((cell) => cell.id));\n\n mxGraphElement.set(mxCellKey, cells);\n mxGraphElement.set(mxCellOrderKey, cellsOrder);\n\n return mxGraphElement as YMxGraphModel;\n}\n\nexport function serialize(map: YMxGraphModel) {\n const cells = map.get(mxCellKey) as unknown as Y.Map<Y.XmlElement>;\n const cellsOrder = map.get(mxCellOrderKey) as unknown as Y.Array<string>;\n return {\n _attributes: {},\n root: {\n [mxCellKey]: cellsOrder\n .toArray()\n .map((id) => serializeMxCell(cells.get(id) as Y.XmlElement)),\n },\n };\n}\n","import * as Y from \"yjs\";\nimport {\n parse as parseMxGraphModel,\n serialize as serializeMxGraphModel,\n key as mxGraphModelKey,\n type MxGraphModel,\n type YMxGraphModel,\n} from \"./mxGraphModel\";\nimport type { ElementCompact } from \"xml-js\";\n\nexport const key = \"diagram\";\n\nexport interface Diagram extends ElementCompact {\n mxGraphModel: MxGraphModel;\n}\n\nexport type YDiagram = Y.Map<unknown>;\n\nexport function parse(object: Diagram): YDiagram {\n const yDiagramElement = new Y.Map();\n yDiagramElement.set(\"name\", `${object._attributes?.name || \"\"}`);\n yDiagramElement.set(\"id\", `${object._attributes?.id || \"\"}`);\n\n const mxGraphModel = parseMxGraphModel(object[mxGraphModelKey]);\n\n yDiagramElement.set(mxGraphModelKey, mxGraphModel);\n return yDiagramElement as YDiagram;\n}\n\nexport function serialize(yDiagram: YDiagram) {\n const mxGraphModel = yDiagram.get(mxGraphModelKey) as unknown as\n | YMxGraphModel\n | undefined;\n\n return {\n _attributes: {\n name: yDiagram.get(\"name\") as unknown as string,\n id: yDiagram.get(\"id\") as unknown as string,\n },\n [mxGraphModelKey]: mxGraphModel\n ? serializeMxGraphModel(mxGraphModel)\n : undefined,\n };\n}\n","import * as Y from \"yjs\";\nimport {\n parse as parseDiagram,\n key as diagramKey,\n serialize as serializeDiagram,\n} from \"./diagram\";\nimport type { Diagram, YDiagram } from \"./diagram\";\nimport type { ElementCompact } from \"xml-js\";\n\nexport const key = \"mxfile\";\nexport const diagramOrderKey = diagramKey + \"Order\";\n\nexport type YMxFile = Y.Map<unknown>;\n\nexport interface MxFile extends ElementCompact {\n diagram: Diagram[];\n}\n\nexport function parse(object: MxFile, doc: Y.Doc) {\n const mxfile = doc.getMap(key);\n mxfile.set(\"pages\", (object._attributes?.pages || \"1\") + \"\");\n\n const diagramList = object.diagram.map((diagram) => ({\n value: parseDiagram(diagram),\n id: (diagram._attributes?.id || \"\") as string,\n }));\n const diagramMap = new Y.Map<YDiagram>();\n const diagramOrder = new Y.Array<string>();\n diagramList.forEach((diagram) => {\n diagramMap.set(diagram.id, diagram.value);\n });\n diagramOrder.push(diagramList.map((diagram) => diagram.id));\n\n mxfile.set(diagramKey, diagramMap);\n mxfile.set(diagramOrderKey, diagramOrder);\n return mxfile;\n}\n\nexport function serializer(yMxFile: YMxFile): ElementCompact {\n const diagrams = yMxFile.get(diagramKey) as unknown as Y.Map<YDiagram>;\n const diagramOrder = yMxFile.get(\n diagramOrderKey,\n ) as unknown as Y.Array<string>;\n\n const orderIds = diagramOrder ? diagramOrder.toArray() : [];\n // 如果 diagramOrder 为空但 diagram map 不为空,使用 diagram map 中的所有 ID\n const ids = orderIds.length > 0 ? orderIds : (diagrams ? Array.from(diagrams.keys()) : []);\n\n const obj: Record<string, unknown> = {\n _attributes: {\n pages: (yMxFile.get(\"pages\") as string) || \"1\",\n },\n [diagramKey]: ids\n .map((id) => diagrams.get(id) as unknown as YDiagram)\n .filter((d): d is YDiagram => !!d)\n .map((diagramElement) => serializeDiagram(diagramElement)),\n };\n\n return obj as ElementCompact;\n}\n","import * as Y from \"yjs\";\nimport { parse, serializer } from \"../helper/xml\";\nimport {\n parse as parseMxFile,\n key as mxfileKey,\n serializer as serializerMxFile,\n type YMxFile,\n} from \"../models/mxfile\";\nimport {\n parse as parseMxGraphModel,\n key as mxGraphModelKey,\n serialize as serializerMxGraphModel,\n type YMxGraphModel,\n} from \"../models/mxGraphModel\";\n\nexport function xml2ydoc(xml: string, doc: Y.Doc): Y.Doc {\n const object = parse(xml);\n\n const mxfile = (object as Record<string, unknown>).mxfile;\n const mxGraphModel = (object as Record<string, unknown>).mxGraphModel;\n if (mxfile) {\n doc.transact(() => {\n parseMxFile(mxfile as import(\"../models/mxfile\").MxFile, doc);\n });\n } else if (mxGraphModel) {\n doc.transact(() => {\n parseMxGraphModel(mxGraphModel as import(\"../models/mxGraphModel\").MxGraphModel, doc);\n });\n } else {\n throw new Error(\"不支持的文件格式\");\n }\n\n return doc;\n}\n\nexport function ydoc2xml(doc: Y.Doc, spaces = 0): string {\n if (doc.share.has(mxfileKey)) {\n return serializer(\n {\n [mxfileKey]: serializerMxFile(\n doc.share.get(mxfileKey) as unknown as YMxFile,\n ),\n },\n spaces,\n );\n } else if (doc.share.has(mxGraphModelKey)) {\n return serializer(\n {\n [mxGraphModelKey]: serializerMxGraphModel(\n doc.share.get(mxGraphModelKey) as unknown as YMxGraphModel,\n ),\n },\n spaces,\n );\n }\n\n return \"\";\n}\n"],"names":["key","parse","xml2js","serializer","js2xml","Y","serialize","mxCellKey","parseMxCell","serializeMxCell","parseMxGraphModel","mxGraphModelKey","serializeMxGraphModel","diagramKey","_a","parseDiagram","serializeDiagram","parseMxFile","mxfileKey","serializerMxFile","serializerMxGraphModel"],"mappings":"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"}
|
package/index-DBU79c0g.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index-DBU79c0g.js","sources":["../src/helper/xml.ts","../src/models/mxCell.ts","../src/models/mxGraphModel.ts","../src/models/diagram.ts","../src/models/mxfile.ts","../src/transform/index.ts"],"sourcesContent":["import { xml2js, js2xml, type ElementCompact } from \"xml-js\";\n\nfunction deepProcess(node: unknown): void {\n if (node == null) return;\n\n if (Array.isArray(node)) {\n for (const item of node) {\n deepProcess(item);\n }\n return;\n }\n\n if (typeof node !== \"object\") return;\n\n const obj = node as Record<string, unknown>;\n const keys = Object.keys(obj);\n for (const key of keys) {\n if (key === \"_attributes\") continue;\n\n let value = obj[key];\n const keyLower = key.toLowerCase();\n\n if (\n (keyLower === \"diagram\" || keyLower === \"mxcell\") &&\n value !== undefined &&\n !Array.isArray(value)\n ) {\n obj[key] = [value];\n value = obj[key];\n }\n\n if (Array.isArray(value)) {\n for (const v of value) deepProcess(v);\n } else if (value && typeof value === \"object\") {\n deepProcess(value);\n }\n }\n}\n\nexport function parse(xml: string) {\n const result = xml2js(xml, { compact: true }) as Record<string, unknown>;\n deepProcess(result);\n return result;\n}\n\nexport function serializer(obj: ElementCompact, spaces = 2) {\n return js2xml(obj, {\n compact: true,\n spaces,\n });\n}\n","import * as Y from \"yjs\";\nimport { xml2js, js2xml } from \"xml-js\";\nimport type { ElementCompact } from \"xml-js\";\n\nexport const key = \"mxCell\";\n\nconst mxGeometryKey = \"mxGeometry\";\nconst mxGeometryAttributeKey = \"geometry\";\n\nexport interface MxCellModel extends ElementCompact {\n [mxGeometryKey]?: ElementCompact;\n}\n\nexport function parse(object: MxCellModel): Y.XmlElement {\n const xmlElement = new Y.XmlElement(\"mxCell\");\n\n for (const attribute of Object.keys(object._attributes || {})) {\n xmlElement.setAttribute(\n attribute,\n `${object._attributes?.[attribute] || \"\"}`\n );\n }\n\n if (object[mxGeometryKey]) {\n const geometry = object[mxGeometryKey];\n const geometryString = js2xml(geometry, {\n compact: true,\n });\n xmlElement.setAttribute(mxGeometryAttributeKey, geometryString);\n delete object[mxGeometryKey];\n }\n\n return xmlElement;\n}\n\nexport function serialize(xmlElement: Y.XmlElement) {\n const rawAttributes = {\n ...xmlElement.getAttributes(),\n };\n\n // 提取 mxGeometry(不需要转义,它本身就是 XML 字符串)\n let mxGeometry: ElementCompact | null = null;\n let mxGeometryString: string | undefined;\n\n if (mxGeometryAttributeKey in rawAttributes) {\n mxGeometryString = rawAttributes[mxGeometryAttributeKey];\n delete rawAttributes[mxGeometryAttributeKey];\n }\n\n // 转义其他属性值中的特殊字符\n const attributes: Record<string, string> = {};\n for (const [key, value] of Object.entries(rawAttributes)) {\n if (typeof value === 'string') {\n attributes[key] = value\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/\"/g, '"')\n .replace(/'/g, ''');\n } else if (value != null) {\n attributes[key] = String(value);\n }\n }\n\n // 解析 mxGeometry\n if (mxGeometryString) {\n try {\n const parsed = xml2js(mxGeometryString, { compact: true }) as Record<string, ElementCompact>;\n mxGeometry = parsed[mxGeometryKey] ?? null;\n if (mxGeometry && mxGeometry._attributes) {\n mxGeometry._attributes[\"as\"] = \"geometry\";\n }\n } catch (e) {\n console.warn(\"[y-mxgraph] Failed to parse mxGeometry:\", e);\n }\n }\n\n const obj: Record<string, unknown> = {\n _attributes: attributes,\n };\n\n if (mxGeometry) {\n obj[mxGeometryKey] = mxGeometry;\n }\n\n return obj;\n}\n","import * as Y from \"yjs\";\n\nimport {\n key as mxCellKey,\n parse as parseMxCell,\n serialize as serializeMxCell,\n type MxCellModel,\n} from \"./mxCell\";\nimport type { ElementCompact } from \"xml-js\";\n\nexport const key = \"mxGraphModel\";\nexport const mxCellOrderKey = mxCellKey + \"Order\";\n\nexport interface MxGraphModel extends ElementCompact {\n root: {\n mxCell: MxCellModel[];\n };\n}\n\nexport type YMxGraphModel = Y.Map<unknown>;\n\nexport function parse(object: MxGraphModel, doc?: Y.Doc) {\n const mxCells = (object.root[mxCellKey] || []).map((cell: MxCellModel) => {\n return {\n value: parseMxCell(cell),\n id: (cell._attributes?.id || \"\") as string,\n };\n });\n\n const mxGraphElement = doc?.getMap(key) || new Y.Map();\n\n const cells = new Y.Map<Y.XmlElement>();\n const cellsOrder = new Y.Array<string>();\n\n mxCells.forEach((cell) => {\n cells.set(cell.id, cell.value);\n });\n\n cellsOrder.push(mxCells.map((cell) => cell.id));\n\n mxGraphElement.set(mxCellKey, cells);\n mxGraphElement.set(mxCellOrderKey, cellsOrder);\n\n return mxGraphElement as YMxGraphModel;\n}\n\nexport function serialize(map: YMxGraphModel) {\n const cells = map.get(mxCellKey) as unknown as Y.Map<Y.XmlElement>;\n const cellsOrder = map.get(mxCellOrderKey) as unknown as Y.Array<string>;\n return {\n _attributes: {},\n root: {\n [mxCellKey]: cellsOrder\n .toArray()\n .map((id) => serializeMxCell(cells.get(id) as Y.XmlElement)),\n },\n };\n}\n","import * as Y from \"yjs\";\nimport {\n parse as parseMxGraphModel,\n serialize as serializeMxGraphModel,\n key as mxGraphModelKey,\n type MxGraphModel,\n type YMxGraphModel,\n} from \"./mxGraphModel\";\nimport type { ElementCompact } from \"xml-js\";\n\nexport const key = \"diagram\";\n\nexport interface Diagram extends ElementCompact {\n mxGraphModel: MxGraphModel;\n}\n\nexport type YDiagram = Y.Map<unknown>;\n\nexport function parse(object: Diagram): YDiagram {\n const yDiagramElement = new Y.Map();\n yDiagramElement.set(\"name\", `${object._attributes?.name || \"\"}`);\n yDiagramElement.set(\"id\", `${object._attributes?.id || \"\"}`);\n\n const mxGraphModel = parseMxGraphModel(object[mxGraphModelKey]);\n\n yDiagramElement.set(mxGraphModelKey, mxGraphModel);\n return yDiagramElement as YDiagram;\n}\n\nexport function serialize(yDiagram: YDiagram) {\n const mxGraphModel = yDiagram.get(mxGraphModelKey) as unknown as\n | YMxGraphModel\n | undefined;\n\n return {\n _attributes: {\n name: yDiagram.get(\"name\") as unknown as string,\n id: yDiagram.get(\"id\") as unknown as string,\n },\n [mxGraphModelKey]: mxGraphModel\n ? serializeMxGraphModel(mxGraphModel)\n : undefined,\n };\n}\n","import * as Y from \"yjs\";\nimport {\n parse as parseDiagram,\n key as diagramKey,\n serialize as serializeDiagram,\n} from \"./diagram\";\nimport type { Diagram, YDiagram } from \"./diagram\";\nimport type { ElementCompact } from \"xml-js\";\n\nexport const key = \"mxfile\";\nexport const diagramOrderKey = diagramKey + \"Order\";\n\nexport type YMxFile = Y.Map<unknown>;\n\nexport interface MxFile extends ElementCompact {\n diagram: Diagram[];\n}\n\nexport function parse(object: MxFile, doc: Y.Doc) {\n const mxfile = doc.getMap(key);\n mxfile.set(\"pages\", (object._attributes?.pages || \"1\") + \"\");\n\n const diagramList = object.diagram.map((diagram) => ({\n value: parseDiagram(diagram),\n id: (diagram._attributes?.id || \"\") as string,\n }));\n const diagramMap = new Y.Map<YDiagram>();\n const diagramOrder = new Y.Array<string>();\n diagramList.forEach((diagram) => {\n diagramMap.set(diagram.id, diagram.value);\n });\n diagramOrder.push(diagramList.map((diagram) => diagram.id));\n\n mxfile.set(diagramKey, diagramMap);\n mxfile.set(diagramOrderKey, diagramOrder);\n return mxfile;\n}\n\nexport function serializer(yMxFile: YMxFile): ElementCompact {\n const diagrams = yMxFile.get(diagramKey) as unknown as Y.Map<YDiagram>;\n const diagramOrder = yMxFile.get(\n diagramOrderKey,\n ) as unknown as Y.Array<string>;\n\n const orderIds = diagramOrder ? diagramOrder.toArray() : [];\n // 如果 diagramOrder 为空但 diagram map 不为空,使用 diagram map 中的所有 ID\n const ids = orderIds.length > 0 ? orderIds : (diagrams ? Array.from(diagrams.keys()) : []);\n\n const obj: Record<string, unknown> = {\n _attributes: {\n pages: (yMxFile.get(\"pages\") as string) || \"1\",\n },\n [diagramKey]: ids\n .map((id) => diagrams.get(id) as unknown as YDiagram)\n .filter((d): d is YDiagram => !!d)\n .map((diagramElement) => serializeDiagram(diagramElement)),\n };\n\n return obj as ElementCompact;\n}\n","import * as Y from \"yjs\";\nimport { parse, serializer } from \"../helper/xml\";\nimport {\n parse as parseMxFile,\n key as mxfileKey,\n serializer as serializerMxFile,\n type YMxFile,\n} from \"../models/mxfile\";\nimport {\n parse as parseMxGraphModel,\n key as mxGraphModelKey,\n serialize as serializerMxGraphModel,\n type YMxGraphModel,\n} from \"../models/mxGraphModel\";\n\nexport function xml2ydoc(xml: string, doc: Y.Doc): Y.Doc {\n const object = parse(xml);\n\n const mxfile = (object as Record<string, unknown>).mxfile;\n const mxGraphModel = (object as Record<string, unknown>).mxGraphModel;\n if (mxfile) {\n doc.transact(() => {\n parseMxFile(mxfile as import(\"../models/mxfile\").MxFile, doc);\n });\n } else if (mxGraphModel) {\n doc.transact(() => {\n parseMxGraphModel(mxGraphModel as import(\"../models/mxGraphModel\").MxGraphModel, doc);\n });\n } else {\n throw new Error(\"不支持的文件格式\");\n }\n\n return doc;\n}\n\nexport function ydoc2xml(doc: Y.Doc, spaces = 0): string {\n if (doc.share.has(mxfileKey)) {\n return serializer(\n {\n [mxfileKey]: serializerMxFile(\n doc.share.get(mxfileKey) as unknown as YMxFile,\n ),\n },\n spaces,\n );\n } else if (doc.share.has(mxGraphModelKey)) {\n return serializer(\n {\n [mxGraphModelKey]: serializerMxGraphModel(\n doc.share.get(mxGraphModelKey) as unknown as YMxGraphModel,\n ),\n },\n spaces,\n );\n }\n\n return \"\";\n}\n"],"names":["key","parse","serializer","serialize","mxCellKey","parseMxCell","serializeMxCell","parseMxGraphModel","mxGraphModelKey","serializeMxGraphModel","diagramKey","_a","parseDiagram","serializeDiagram","parseMxFile","mxfileKey","serializerMxFile","serializerMxGraphModel"],"mappings":"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"}
|