Files
ST-Bionic-Memory-Ecology/ui/graph-layout-worker.js
2026-04-13 16:11:43 +08:00

142 lines
3.8 KiB
JavaScript

import { solveLayoutWithJs } from "./graph-layout-solver.js";
let nativeSolver = null;
let nativeLoadAttempted = false;
let nativeLoadError = "";
let readNativeModuleStatus = null;
const canceledJobIds = new Set();
const activeJobIds = new Set();
async function ensureNativeSolver() {
if (nativeLoadAttempted) return nativeSolver;
nativeLoadAttempted = true;
try {
const nativeModule = await import("../vendor/wasm/stbme_core.js");
const solveLayout =
nativeModule?.solveLayout || nativeModule?.default?.solveLayout || null;
nativeSolver = typeof solveLayout === "function" ? solveLayout : null;
readNativeModuleStatus =
typeof nativeModule?.getNativeModuleStatus === "function"
? nativeModule.getNativeModuleStatus
: null;
} catch (error) {
nativeLoadError = error?.message || String(error);
nativeSolver = null;
}
return nativeSolver;
}
async function solveLayout(payload = {}) {
const nativeRequested = payload?.nativeRequested === true;
if (nativeRequested) {
const solver = await ensureNativeSolver();
if (solver) {
try {
const nativeResult = await solver(payload);
const nativeStatus =
typeof readNativeModuleStatus === "function"
? readNativeModuleStatus()
: null;
if (
nativeResult &&
nativeResult.ok === true &&
nativeResult.positions instanceof Float32Array
) {
return {
...nativeResult,
usedNative: true,
diagnostics: {
...(nativeResult.diagnostics || {}),
solver: "rust-wasm",
moduleSource: String(nativeStatus?.source || ""),
nativeLoadError: String(nativeStatus?.error || nativeLoadError || ""),
},
};
}
} catch (error) {
return {
ok: false,
skipped: true,
reason: "native-solver-failed",
error: error?.message || String(error),
nativeLoadError,
};
}
}
}
const jsResult = solveLayoutWithJs(payload);
return {
...jsResult,
usedNative: false,
nativeLoadError,
};
}
self.addEventListener("message", async (event) => {
const message = event?.data || {};
if (message.type === "cancel-layout") {
const canceledJobId = Number(message.jobId);
if (Number.isFinite(canceledJobId)) {
canceledJobIds.add(canceledJobId);
}
return;
}
if (message.type !== "solve-layout") return;
const jobId = Number(message.jobId);
if (!Number.isFinite(jobId)) return;
if (canceledJobIds.has(jobId)) {
canceledJobIds.delete(jobId);
return;
}
activeJobIds.add(jobId);
const fallbackErrorResult = (errorMessage = "unknown-worker-error") => ({
ok: false,
skipped: true,
reason: "worker-exception",
error: String(errorMessage || "unknown-worker-error"),
});
try {
const result = await solveLayout(message.payload || {});
if (!activeJobIds.has(jobId) || canceledJobIds.has(jobId)) {
activeJobIds.delete(jobId);
canceledJobIds.delete(jobId);
return;
}
activeJobIds.delete(jobId);
if (result?.positions instanceof Float32Array) {
self.postMessage(
{
type: "layout-result",
jobId,
result,
},
[result.positions.buffer],
);
return;
}
self.postMessage({
type: "layout-result",
jobId,
result,
});
} catch (error) {
if (!activeJobIds.has(jobId) || canceledJobIds.has(jobId)) {
activeJobIds.delete(jobId);
canceledJobIds.delete(jobId);
return;
}
activeJobIds.delete(jobId);
self.postMessage({
type: "layout-result",
jobId,
result: fallbackErrorResult(error?.message || String(error)),
});
}
});