ELK runs in a worker: the flow layout loads elkjs's worker build as a Vite ?worker behind elk-api, so a phone's eight layout passes no longer block the page (Demo house at 390px with 4x CPU throttling, long tasks 15.0 to 0.8 s, longest 2.1 to 0.3 s); positions and routes unchanged

This commit is contained in:
2026-10-15 21:37:31 +02:00
parent ce2ce4e3e7
commit dc2802d4c0
2 changed files with 23 additions and 3 deletions
@@ -14,6 +14,20 @@ import assert from "node:assert/strict"
// @ts-expect-error the runtime has it, the DOM types say it is not removable.
globalThis.self = undefined
// A page runs ELK in a worker Vite builds (`?worker`). Here the same build
// runs in-thread, as the fake worker it exports outside a worker.
// @ts-expect-error the worker build ships without types.
const { Worker: InThread } = await import("elkjs/lib/elk-worker.min.js")
// @ts-expect-error bun's global, which the DOM types do not know.
Bun.plugin({
name: "elk in-thread",
setup: (build: { module: (id: string, load: () => object) => void }) =>
build.module("elkjs/lib/elk-worker.min.js?worker", () => ({
exports: { default: InThread },
loader: "object",
})),
})
const { layoutGraph, nodeHeight, portFraction, PORT_SPAN } = await import(
"./layout"
)
+9 -3
View File
@@ -384,14 +384,20 @@ function wrapWideRanks(placed: Map<string, Point>, ids: string[]): Wrap[] {
* The layout engine, fetched the first time a flow needs laying out.
*
* Its own chunk: ELK is a compiled Java library and weighs more than the rest
* of the canvas put together, so nothing but this route ever loads it.
* of the canvas put together, so nothing but this route ever loads it. And
* its own thread: a phone lays the Demo house out eight times over, about
* half a second each, which on the page would freeze it for seconds.
*/
let engine: Promise<{ layout: (graph: ElkNode) => Promise<ElkNode> }> | null =
null
function elk() {
engine ??= import("elkjs/lib/elk.bundled.js").then(
(module) => new module.default(),
engine ??= Promise.all([
import("elkjs/lib/elk-api.js"),
import("elkjs/lib/elk-worker.min.js?worker"),
]).then(
([api, worker]) =>
new api.default({ workerFactory: () => new worker.default() }),
)
return engine
}