Your Name 1912bcd25b
Some checks failed
Lint / build (20) (push) Has been cancelled
Playwright Tests / test (push) Has been cancelled
chore: sync all remaining local workspace changes
2026-05-08 11:26:57 +08:00

98 lines
2.6 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* Draft Save Worker
*
* 在 Web Worker 中执行草稿序列化和指纹计算,避免阻塞主线程。
* 主线程通过 postMessage 传入 canvas 快照,Worker 返回序列化后的 payload。
*
* 放置在 public/ 下以从同源加载,避免 assetPrefix (CDN) 导致的跨域 Worker 加载失败。
*/
let lastFingerprint = "";
/** Canonical source: src/views/canvas/workers/draftSaveUtils.ts — keep in sync */
function djb2(str) {
let hash = 5381;
for (let i = 0; i < str.length; i++) {
hash = ((hash << 5) + hash + str.charCodeAt(i)) | 0;
}
return hash >>> 0;
}
/**
* 计算草稿指纹:O(n log n) 排序保证一致性
*/
function computeFingerprint(nodes, edges, viewport) {
const nodeFingerprints = nodes
.map(
(n) =>
`${n.id}:${n.position?.x ?? 0},${n.position?.y ?? 0}:${typeof n.data === "object" ? djb2(JSON.stringify(n.data)) : 0}`,
)
.sort()
.join("|");
const edgeFingerprints = edges
.map((e) => `${e.id}:${e.source}-${e.target}`)
.sort()
.join("|");
const vp = `V${Math.round(viewport.position.x)},${Math.round(viewport.position.y)},${viewport.zoom.toFixed(2)}`;
return `N${nodes.length}E${edges.length}:${vp}:${nodeFingerprints}::${edgeFingerprints}`;
}
/**
* 清理 blob:/data: URL,避免序列化无效引用
*/
function sanitizeDraftJson(draftJson) {
try {
const data = JSON.parse(draftJson);
return JSON.stringify(data, (_key, value) => {
if (
typeof value === "string" &&
(value.startsWith("blob:") || value.startsWith("data:"))
) {
return "";
}
return value;
});
} catch {
return draftJson;
}
}
self.onmessage = (e) => {
try {
const { nodes, edges, viewport, projectUuid, syncVersion } = e.data;
const fingerprint = computeFingerprint(nodes, edges, viewport);
if (fingerprint === lastFingerprint) {
self.postMessage({ type: "result", fingerprint, payload: null });
return;
}
lastFingerprint = fingerprint;
const draftData = {
nodes,
edges,
savedAt: Date.now(),
version: syncVersion,
};
const draftJson = sanitizeDraftJson(JSON.stringify(draftData));
self.postMessage({
type: "result",
fingerprint,
payload: {
projectUuid,
draftJson,
viewportX: viewport.position.x.toString(),
viewportY: viewport.position.y.toString(),
viewportZoom: viewport.zoom.toString(),
},
});
} catch (err) {
self.postMessage({
type: "error",
message: err instanceof Error ? err.message : String(err),
});
}
};