y-mxgraph 0.6.5 → 0.6.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -0
- package/README.zh-CN.md +3 -1
- package/binding/collaborator/cursor.d.ts.map +1 -1
- package/binding/collaborator/index.d.ts +3 -0
- package/binding/collaborator/index.d.ts.map +1 -1
- package/binding/index.d.ts +1 -0
- package/binding/index.d.ts.map +1 -1
- package/iframe-bridge/provider.cjs +37 -17
- package/iframe-bridge/provider.cjs.map +1 -1
- package/iframe-bridge/provider.js +37 -17
- package/iframe-bridge/provider.js.map +1 -1
- package/iframe-bridge/server.cjs +35 -1
- package/iframe-bridge/server.cjs.map +1 -1
- package/iframe-bridge/server.js +35 -1
- package/iframe-bridge/server.js.map +1 -1
- package/package.json +1 -1
- package/y-mxgraph.cjs +16 -3
- package/y-mxgraph.cjs.map +1 -1
- package/y-mxgraph.js +16 -3
- package/y-mxgraph.js.map +1 -1
package/iframe-bridge/server.js
CHANGED
|
@@ -3,11 +3,40 @@ import { encodeAwarenessUpdate, applyAwarenessUpdate } from "y-protocols/awarene
|
|
|
3
3
|
const IFRAME_ORIGIN = {};
|
|
4
4
|
const BASELINE_ORIGIN = {};
|
|
5
5
|
function createIframeBridgeServer(iframe, ydoc, awareness, options) {
|
|
6
|
-
const { undoManager } = options != null ? options : {};
|
|
6
|
+
const { undoManager, awarenessSyncMode = "binary" } = options != null ? options : {};
|
|
7
7
|
let connected = false;
|
|
8
8
|
let applyingIframeUpdate = false;
|
|
9
9
|
const connectListeners = /* @__PURE__ */ new Set();
|
|
10
10
|
const disconnectListeners = /* @__PURE__ */ new Set();
|
|
11
|
+
let iframeOriginTracked = false;
|
|
12
|
+
function tryAddIframeOriginTracking() {
|
|
13
|
+
if (!undoManager) return;
|
|
14
|
+
try {
|
|
15
|
+
if (typeof undoManager.addTrackedOrigin === "function") {
|
|
16
|
+
undoManager.addTrackedOrigin(IFRAME_ORIGIN);
|
|
17
|
+
iframeOriginTracked = true;
|
|
18
|
+
}
|
|
19
|
+
} catch (error) {
|
|
20
|
+
console.warn(
|
|
21
|
+
"[iframe-bridge server] failed to add IFRAME_ORIGIN to UndoManager tracked origins:",
|
|
22
|
+
error
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
function tryRemoveIframeOriginTracking() {
|
|
27
|
+
if (!undoManager || !iframeOriginTracked) return;
|
|
28
|
+
try {
|
|
29
|
+
if (typeof undoManager.removeTrackedOrigin === "function") {
|
|
30
|
+
undoManager.removeTrackedOrigin(IFRAME_ORIGIN);
|
|
31
|
+
}
|
|
32
|
+
} catch (error) {
|
|
33
|
+
console.warn(
|
|
34
|
+
"[iframe-bridge server] failed to remove IFRAME_ORIGIN from UndoManager tracked origins:",
|
|
35
|
+
error
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
tryAddIframeOriginTracking();
|
|
11
40
|
function setConnected(value) {
|
|
12
41
|
if (connected === value) return;
|
|
13
42
|
connected = value;
|
|
@@ -102,6 +131,10 @@ function createIframeBridgeServer(iframe, ydoc, awareness, options) {
|
|
|
102
131
|
const isBaseline = event.data.isBaseline;
|
|
103
132
|
const applyOrigin = isBaseline ? BASELINE_ORIGIN : IFRAME_ORIGIN;
|
|
104
133
|
Y.applyUpdate(ydoc, update, applyOrigin);
|
|
134
|
+
} else if (msgType === "awareness-local-state") {
|
|
135
|
+
applyingIframeUpdate = true;
|
|
136
|
+
awareness.setLocalState(event.data.state);
|
|
137
|
+
applyingIframeUpdate = false;
|
|
105
138
|
} else if (msgType === "awareness-update") {
|
|
106
139
|
applyingIframeUpdate = true;
|
|
107
140
|
applyAwarenessUpdate(awareness, new Uint8Array(payload), IFRAME_ORIGIN);
|
|
@@ -162,6 +195,7 @@ function createIframeBridgeServer(iframe, ydoc, awareness, options) {
|
|
|
162
195
|
undoManager.off("stack-item-popped", onUndoPopped);
|
|
163
196
|
undoManager.off("stack-cleared", onStackCleared);
|
|
164
197
|
undoManager.off("stack-item-added", onStackItemAdded);
|
|
198
|
+
tryRemoveIframeOriginTracking();
|
|
165
199
|
}
|
|
166
200
|
connectListeners.clear();
|
|
167
201
|
disconnectListeners.clear();
|
|
@@ -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 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":"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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 awarenessSyncMode?: \"binary\" | \"local-state\";\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, awarenessSyncMode = \"binary\" } = options ?? {};\n let connected = false;\n let applyingIframeUpdate = false;\n const connectListeners = new Set<() => void>();\n const disconnectListeners = new Set<() => void>();\n let iframeOriginTracked = false;\n\n function tryAddIframeOriginTracking() {\n if (!undoManager) return;\n try {\n if (typeof (undoManager as any).addTrackedOrigin === \"function\") {\n (undoManager as any).addTrackedOrigin(IFRAME_ORIGIN);\n iframeOriginTracked = true;\n }\n } catch (error) {\n console.warn(\n \"[iframe-bridge server] failed to add IFRAME_ORIGIN to UndoManager tracked origins:\",\n error,\n );\n }\n }\n\n function tryRemoveIframeOriginTracking() {\n if (!undoManager || !iframeOriginTracked) return;\n try {\n if (typeof (undoManager as any).removeTrackedOrigin === \"function\") {\n (undoManager as any).removeTrackedOrigin(IFRAME_ORIGIN);\n }\n } catch (error) {\n console.warn(\n \"[iframe-bridge server] failed to remove IFRAME_ORIGIN from UndoManager tracked origins:\",\n error,\n );\n }\n }\n\n tryAddIframeOriginTracking();\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-local-state\") {\n applyingIframeUpdate = true;\n awareness.setLocalState(event.data.state);\n applyingIframeUpdate = false;\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 tryRemoveIframeOriginTracking();\n }\n connectListeners.clear();\n disconnectListeners.clear();\n },\n };\n}\n"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
package/y-mxgraph.cjs
CHANGED
|
@@ -932,7 +932,7 @@ function createCursorEl(color, username) {
|
|
|
932
932
|
name.style.padding = "3px 7px";
|
|
933
933
|
name.style.marginTop = "8px";
|
|
934
934
|
name.style.borderRadius = "10px";
|
|
935
|
-
name.style.maxWidth = "
|
|
935
|
+
name.style.maxWidth = "140px";
|
|
936
936
|
name.style.overflow = "hidden";
|
|
937
937
|
name.style.textOverflow = "ellipsis";
|
|
938
938
|
name.style.whiteSpace = "nowrap";
|
|
@@ -1028,7 +1028,7 @@ function renderRemoteCursors(ui, remotes) {
|
|
|
1028
1028
|
const graph = ui.editor.graph;
|
|
1029
1029
|
const { translate, scale } = graph.view;
|
|
1030
1030
|
currentPageRemotes.forEach(
|
|
1031
|
-
({ clientId, cursorState, userColor, userName }) => {
|
|
1031
|
+
({ clientId, cursorState, userColor, userName, userAccount }) => {
|
|
1032
1032
|
if (!cursorState) return;
|
|
1033
1033
|
let el = cache.get(clientId);
|
|
1034
1034
|
if (cursorState.hide) {
|
|
@@ -1163,6 +1163,7 @@ function renderRemoteSelections(ui, remotes) {
|
|
|
1163
1163
|
}
|
|
1164
1164
|
const DEFAULT_USER_NAME_KEY = "user.name";
|
|
1165
1165
|
const DEFAULT_USER_COLOR_KEY = "user.color";
|
|
1166
|
+
const DEFAULT_USER_ACCOUNT_KEY = "user.account";
|
|
1166
1167
|
function bindCollaborator(file, options) {
|
|
1167
1168
|
var _a, _b;
|
|
1168
1169
|
const graph = options.graph || file.getUi().editor.graph;
|
|
@@ -1171,6 +1172,7 @@ function bindCollaborator(file, options) {
|
|
|
1171
1172
|
const cursorOption = options.cursor;
|
|
1172
1173
|
const userNameKey = typeof cursorOption === "object" && (cursorOption == null ? void 0 : cursorOption.userNameKey) ? cursorOption.userNameKey : DEFAULT_USER_NAME_KEY;
|
|
1173
1174
|
const userColorKey = typeof cursorOption === "object" && (cursorOption == null ? void 0 : cursorOption.userColorKey) ? cursorOption.userColorKey : DEFAULT_USER_COLOR_KEY;
|
|
1175
|
+
const userAccountKey = typeof cursorOption === "object" && (cursorOption == null ? void 0 : cursorOption.userAccountKey) ? cursorOption.userAccountKey : DEFAULT_USER_ACCOUNT_KEY;
|
|
1174
1176
|
let userName = getAwarenessStateValue(awareness, userNameKey);
|
|
1175
1177
|
if (!userName) {
|
|
1176
1178
|
userName = generateRandomName();
|
|
@@ -1181,6 +1183,7 @@ function bindCollaborator(file, options) {
|
|
|
1181
1183
|
userColor = generateColor(userName);
|
|
1182
1184
|
setAwarenessStateValue(awareness, userColorKey, userColor);
|
|
1183
1185
|
}
|
|
1186
|
+
getAwarenessStateValue(awareness, userAccountKey);
|
|
1184
1187
|
const cleanupCursor = bindCursor(file, {
|
|
1185
1188
|
awareness,
|
|
1186
1189
|
graph,
|
|
@@ -1204,6 +1207,10 @@ function bindCollaborator(file, options) {
|
|
|
1204
1207
|
userColorKey
|
|
1205
1208
|
);
|
|
1206
1209
|
if (newColor) userColor = newColor;
|
|
1210
|
+
getAwarenessStateValue(
|
|
1211
|
+
awareness,
|
|
1212
|
+
userAccountKey
|
|
1213
|
+
);
|
|
1207
1214
|
}
|
|
1208
1215
|
const remotes = /* @__PURE__ */ new Map();
|
|
1209
1216
|
for (const [clientId] of states.entries()) {
|
|
@@ -1211,6 +1218,11 @@ function bindCollaborator(file, options) {
|
|
|
1211
1218
|
if (!changedClientIds.has(clientId)) continue;
|
|
1212
1219
|
const name = getAwarenessStateValue(awareness, userNameKey, clientId) || clientId + "";
|
|
1213
1220
|
const color = getAwarenessStateValue(awareness, userColorKey, clientId) || "#000000";
|
|
1221
|
+
const account = getAwarenessStateValue(
|
|
1222
|
+
awareness,
|
|
1223
|
+
userAccountKey,
|
|
1224
|
+
clientId
|
|
1225
|
+
);
|
|
1214
1226
|
remotes.set(clientId, {
|
|
1215
1227
|
clientId,
|
|
1216
1228
|
cursorState: getAwarenessStateValue(
|
|
@@ -1224,7 +1236,8 @@ function bindCollaborator(file, options) {
|
|
|
1224
1236
|
clientId
|
|
1225
1237
|
),
|
|
1226
1238
|
userColor: color,
|
|
1227
|
-
userName: name
|
|
1239
|
+
userName: name,
|
|
1240
|
+
userAccount: account || void 0
|
|
1228
1241
|
});
|
|
1229
1242
|
}
|
|
1230
1243
|
renderRemoteCursors(file.getUi(), remotes);
|