Skip to content

feat(joint-layout-elk): new package - ELK layout with container and port support - #3497

Open
Geliogabalus wants to merge 75 commits into
clientIO:masterfrom
Geliogabalus:layout-elk
Open

Geliogabalus wants to merge 75 commits into
clientIO:masterfrom
Geliogabalus:layout-elk

Conversation

@Geliogabalus

@Geliogabalus Geliogabalus commented Sep 7, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Introduces @joint/layout-elk, a new package that lays out JointJS graphs with the Eclipse Layout Kernel (ELK) through elkjs. The single entry point is the async layout({ graph, elements?, links? }, options?).

layout() runs once and does not keep the graph laid out. It converts the graph to an ELK graph, runs ELK, and writes the result back onto the graph: positions, container sizes, link vertices and anchors, and port and label positions. All of it is applied in a single graph batch (batchName, default 'layout'), and the batch is closed even if a callback throws.

What gets laid out

  • Containers - embedded elements are laid out inside their parent at any nesting depth, and the parent is resized to fit them (elk.hierarchyHandling: INCLUDE_CHILDREN). A link is filed under the lowest common ancestor of its two ends. Edge coordinates come back graph-absolute (elk.json.edgeCoords: ROOT).
  • Ports - elements with ports are exported with elk.portConstraints: FIXED_POS, so ports stay where JointJS's port layout places them and edges route to that spot. Override it per element (e.g. FIXED_SIDE) to let ELK move them. Port labels are sized through exportPortLabel.
  • Link labels - laid out inline on their edge and written back as position.distance/offset. Each ELK label carries the index of its link label in its id, so the result goes back to the right label even when some labels are dropped from the layout.
  • Selection and order - elements/links choose what is laid out (a link only if both its ends are). The order of each list is also the model order ELK sees.

Customization

  • Export callbacks (exportElement, exportPort, exportPortLabel, exportLink, exportLinkLabel) receive the ELK draft this package computed for that element, port, link or label. Mutate it in place (e.g. its layoutOptions), or return false to drop it from the layout.
  • Import callbacks (setElementAttributes, setPortAttributes, setLinkAttributes) replace the default set()/portProp() calls - e.g. to animate the result with transition().
  • elkLayoutOptions are passed to ELK unmodified. The package ships TypeScript types for the slice of ELK's options it uses.
  • The result includes the layout's bounding box (bbox) and the raw ELK result (elkGraph).

Main thread, Web Worker and aborting

  • By default, ELK runs on the main thread - no setup, and it works anywhere: browsers, Node/SSR, tests and the UMD build. The main-thread copy of ELK (elkjs/lib/elk.bundled.js) is imported dynamically, so bundlers split it into its own chunk, loaded only by the first layout without an elk option.
  • createWorkerElk(createWorker) returns an ELK instance running in a Web Worker the app starts, passed to layout() as elk. The worker script is published as @joint/layout-elk/worker, so elkjs resolves from this package rather than from the app:
    const elk = createWorkerElk(() => new Worker(new URL('@joint/layout-elk/worker', import.meta.url), { type: 'module' }));
    await layout({ graph }, { elk });
    • The worker starts on the first layout and is shared by every later one.
    • A crash rejects only the layout the worker was running; a new worker takes over the layouts still waiting.
    • A layout whose graph can't be sent to the worker (e.g. a non-cloneable value left in it by an export callback) is rejected on its own, without holding up the others.
    • A worker that fails to load rejects its layouts with an explicit error instead of silently falling back to the main thread.
    • terminate() stops the worker and rejects the layouts not settled yet.
    • The instance talks to ELK's worker script directly rather than through elkjs/lib/elk-api.js, which can neither cancel a layout nor settle one whose worker fails.
  • signal aborts a layout: layout() rejects with the signal's reason and applies nothing to the graph. If a createWorkerElk() worker is running the aborted layout, the worker is terminated. Main-thread ELK, or any other elk instance, can't be stopped, so its result is only ignored.

The app starts the worker itself because how a worker script is located depends on the bundler. While this PR was in progress, the package started its own worker, and that needed a separate workaround for Vite's dev server, esbuild, absolute publicPaths, the UMD build and CSP. The README documents the setup for webpack 5 and Vite, and for running without a bundler.

@joint/core changes

  • dia.Element type fixes - portProp(portId) / portProp(portId, object, opt) overloads, and PortLabelPositionType. layout-elk writes port positions with portProp(portId, object).

layout-elk sizes link labels from the model, without a view, through dia.Link#getComputedLabels() - the label resolution against defaultLabel and the built-in default from #3528, already on master. That method is protected in @joint/core's types for now and will become public in the future, so layout-elk calls it under a // @ts-expect-error comment saying so. Once the method is public, TypeScript reports the directive as unused, a reminder to remove it.

Examples

  • layout-elk-ts - migrated to layout() (previously hand-rolled ELK export/import).
  • layout-elk-default-ts (new) - layout() at its simplest: no callbacks, ELK on the main thread.
  • layout-elk-containers-ports-ts (new) - nested containers, ports with labels, and a wrapped, width-restricted layout.
  • layout-elk-rectpacking-ts (new) - ELK's rectpacking algorithm on folders and files, with toolbar options and an animated transition.

All examples except layout-elk-default-ts run ELK in a Web Worker via createWorkerElk().

Test plan

  • yarn test in packages/joint-layout-elk - 53/53 passing, coverage thresholds met (100% functions). This covers:
    • containers, ports, link labels, selection and order
    • the export/import callbacks, including each one's false return
    • batching
    • abort (main thread, worker, a custom instance, and a null reason)
    • createWorkerElk() - shared worker, aborting a running or a waiting layout, a crash (including one during an aborted layout), a layout that can't be sent to the worker, load failure, a worker that can't be started, terminate()
    • loading main-thread ELK again after it failed to load
  • yarn lint in packages/joint-layout-elk - clean
  • All four examples built with webpack 5 and loaded in headless Chrome from a sub-path. Each lays out every element. The worker examples load only the worker files, never the main-thread chunk. The default example runs on the main thread without starting a worker.
  • createWorkerElk() setup checked in real builds:
    • new Worker(new URL('@joint/layout-elk/worker', import.meta.url), { type: 'module' }) - webpack 5, Vite dev server, Vite build
    • import ElkWorker from '@joint/layout-elk/worker?worker' - Vite dev server, Vite build
  • yarn test-ts in packages/joint-core - passing (covers the portProp() overloads and PortLabelPositionType)
  • yarn install --immutable - clean
  • yarn changeset status --since=upstream/master - @joint/layout-elk minor (new package, 4.3.0 → 4.4.0), @joint/core patch (the port type fixes)

🤖 Generated with Claude Code

Comment thread examples/layout-elk-containers-ports-ts/src/example.ts Outdated
Comment thread packages/joint-core/src/dia/Link.mjs
Comment thread packages/joint-core/src/dia/Link.mjs Outdated
Comment thread packages/joint-core/types/dia.d.ts Outdated
Comment thread packages/joint-core/src/dia/Link.mjs Outdated
Comment thread packages/joint-core/src/dia/Link.mjs Outdated
Comment thread packages/joint-core/src/dia/LinkView.mjs Outdated
Comment thread packages/joint-core/src/dia/LinkView.mjs Outdated
Comment thread packages/joint-core/src/linkTools/RotateLabel.mjs Outdated
@Geliogabalus
Geliogabalus requested review from kumilingus and a balanced review from Copilot October 8, 2026 08:49

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Export callback handling, worker failure cleanup, abort reasons, and new flowchart port connectivity contain unresolved functional issues.

4 open findings
What changed in this PR

Introduces @joint/layout-elk for asynchronous ELK-based JointJS graph layout, including containers, ports, labels, workers, cancellation, and examples.

Changes:

  • Adds the ELK layout package, public types, worker support, tests, and documentation.
  • Adds computed link-label APIs and port typing fixes to @joint/core.
  • Adds four demos and migrates the existing ELK demo.
File Description
.changeset/​brave-labels-resolve.md Records computed-label APIs.
.changeset/​layout-elk-new-package.md Records the new package.
.changeset/​port-label-position-type.md Records the port-label type fix.
.changeset/​port-prop-overloads.md Records new portProp() overloads.
package.json Adds ELK package packing.
yarn.lock Registers package and demos.
packages/​joint-core/​types/​dia.d.ts Adds label and port types.
packages/​joint-core/​test/​ts/​index.test.ts Tests TypeScript port APIs.
packages/​joint-core/​test/​jointjs/​linkView.js Tests invalid label positions.
packages/​joint-core/​test/​jointjs/​links.js Tests computed labels.
packages/​joint-core/​src/​linkTools/​RotateLabel.mjs Uses computed labels.
packages/​joint-core/​src/​dia/​LinkView.mjs Centralizes label resolution.
packages/​joint-core/​src/​dia/​Link.mjs Exposes computed-label methods.
packages/​joint-core/​src/​dia/​link-labels.mjs Implements label resolution.
packages/​joint-layout-elk/​LICENSE Supplies package license.
packages/​joint-layout-elk/​.gitignore Ignores generated output.
packages/​joint-layout-elk/​CHANGELOG.md Initializes the changelog.
packages/​joint-layout-elk/​README.md Documents package usage.
packages/​joint-layout-elk/​SECURITY.md Documents security reporting.
packages/​joint-layout-elk/​coverage.json Defines coverage thresholds.
packages/​joint-layout-elk/​eslint.config.mjs Configures linting.
packages/​joint-layout-elk/​karma.conf.js Configures browser tests.
packages/​joint-layout-elk/​package.json Defines package publication and builds.
packages/​joint-layout-elk/​rollup.config.mjs Builds UMD and test bundles.
packages/​joint-layout-elk/​tsconfig.json Defines base TypeScript settings.
packages/​joint-layout-elk/​tsconfig.cjs.json Defines CommonJS compilation.
packages/​joint-layout-elk/​tsconfig.esm.json Defines ESM compilation.
packages/​joint-layout-elk/​test/​index.html Adds a browser test harness.
packages/​joint-layout-elk/​test/​index.js Tests layout and worker behavior.
packages/​joint-layout-elk/​src/​abort.mts Implements cancellation helpers.
packages/​joint-layout-elk/​src/​defaultElk.mts Manages shared main-thread ELK.
packages/​joint-layout-elk/​src/​elk.worker.mts Publishes the ELK worker entry.
packages/​joint-layout-elk/​src/​export.mts Converts JointJS graphs to ELK.
packages/​joint-layout-elk/​src/​import.mts Applies ELK results to JointJS.
packages/​joint-layout-elk/​src/​index.mts Exposes the public API.
packages/​joint-layout-elk/​src/​labelIds.mts Maps ELK labels to link labels.
packages/​joint-layout-elk/​src/​layout.mts Implements the layout entry point.
packages/​joint-layout-elk/​src/​mainThreadElk.mts Dynamically loads ELK.
packages/​joint-layout-elk/​src/​workerElk.mts Implements worker lifecycle handling.
packages/​joint-layout-elk/​src/​types/​index.mts Exports ELK-facing types.
packages/​joint-layout-elk/​src/​types/​elkEdgeOptions.mts Types edge options.
packages/​joint-layout-elk/​src/​types/​elkEnums.mts Types ELK enum values.
packages/​joint-layout-elk/​src/​types/​elkGraph.mts Narrows ELK graph types.
packages/​joint-layout-elk/​src/​types/​elkLabelOptions.mts Types label options.
packages/​joint-layout-elk/​src/​types/​elkLayoutOptions.mts Types root layout options.
packages/​joint-layout-elk/​src/​types/​elkNodeOptions.mts Types node options.
packages/​joint-layout-elk/​src/​types/​elkPortOptions.mts Types port options.
examples/​layout-elk-ts/​package.json Uses the new package.
examples/​layout-elk-ts/​README.md Updates demo documentation.
examples/​layout-elk-ts/​src/​index.ts Migrates to layout().
examples/​layout-elk-ts/​tsconfig.json Updates TypeScript settings.
examples/​layout-elk-ts/​webpack.config.js Bundles the worker correctly.
examples/​layout-elk-default-ts/​.gitignore Ignores demo output.
examples/​layout-elk-default-ts/​index.html Adds default-demo markup.
examples/​layout-elk-default-ts/​package.json Defines default-demo dependencies.
examples/​layout-elk-default-ts/​README.md Documents the default demo.
examples/​layout-elk-default-ts/​src/​example.ts Defines the example graph.
examples/​layout-elk-default-ts/​src/​index.ts Demonstrates default layout.
examples/​layout-elk-default-ts/​src/​shapes.ts Defines demo shapes.
examples/​layout-elk-default-ts/​src/​styles.scss Styles the default demo.
examples/​layout-elk-default-ts/​tsconfig.json Configures TypeScript.
examples/​layout-elk-default-ts/​webpack.config.js Configures bundling.
examples/​layout-elk-containers-ports-ts/​.gitignore Ignores demo output.
examples/​layout-elk-containers-ports-ts/​index.html Adds demo markup.
examples/​layout-elk-containers-ports-ts/​package.json Defines demo dependencies.
examples/​layout-elk-containers-ports-ts/​README.md Documents container layout.
examples/​layout-elk-containers-ports-ts/​src/​example.ts Defines a nested graph.
examples/​layout-elk-containers-ports-ts/​src/​index.ts Customizes ports and containers.
examples/​layout-elk-containers-ports-ts/​src/​shapes.ts Defines container demo shapes.
examples/​layout-elk-containers-ports-ts/​src/​styles.scss Styles the container demo.
examples/​layout-elk-containers-ports-ts/​tsconfig.json Configures TypeScript.
examples/​layout-elk-containers-ports-ts/​webpack.config.js Configures worker bundling.
examples/​layout-elk-flowchart-ts/​.gitignore Ignores demo output.
examples/​layout-elk-flowchart-ts/​index.html Adds flowchart markup.
examples/​layout-elk-flowchart-ts/​package.json Defines flowchart dependencies.
examples/​layout-elk-flowchart-ts/​README.md Documents interactions.
examples/​layout-elk-flowchart-ts/​src/​example.ts Defines the flowchart graph.
examples/​layout-elk-flowchart-ts/​src/​index.ts Implements interactive layout.
examples/​layout-elk-flowchart-ts/​src/​shapes.ts Defines flowchart shapes.
examples/​layout-elk-flowchart-ts/​src/​styles.scss Styles the flowchart.
examples/​layout-elk-flowchart-ts/​tsconfig.json Configures TypeScript.
examples/​layout-elk-flowchart-ts/​webpack.config.js Configures worker bundling.
examples/​layout-elk-rectpacking-ts/​.gitignore Ignores demo output.
examples/​layout-elk-rectpacking-ts/​index.html Adds packing controls.
examples/​layout-elk-rectpacking-ts/​package.json Defines demo dependencies.
examples/​layout-elk-rectpacking-ts/​README.md Documents rectangle packing.
examples/​layout-elk-rectpacking-ts/​src/​example.ts Defines folder/file data.
examples/​layout-elk-rectpacking-ts/​src/​index.ts Implements interactive packing.
examples/​layout-elk-rectpacking-ts/​src/​shapes.ts Defines packing shapes.
examples/​layout-elk-rectpacking-ts/​src/​styles.scss Styles the packing demo.
examples/​layout-elk-rectpacking-ts/​tsconfig.json Configures TypeScript.
examples/​layout-elk-rectpacking-ts/​webpack.config.js Configures worker bundling.

🧠 Review effort: Balanced


Give feedback about Copilot approvals in this survey to enter a drawing for a $150 gift card.

Comment thread examples/layout-elk-flowchart-ts/src/index.ts Outdated
Comment thread packages/joint-layout-elk/src/abort.mts Outdated
Comment thread packages/joint-layout-elk/src/export.mts Outdated
Comment thread packages/joint-layout-elk/src/workerElk.mts

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment thread packages/joint-layout-elk/src/export.mts Outdated
Comment thread examples/layout-elk-containers-ports-ts/index.html
Comment thread examples/layout-elk-default-ts/index.html
Comment thread examples/layout-elk-rectpacking-ts/index.html
Comment thread packages/joint-core/src/dia/LinkView.mjs
Comment thread packages/joint-layout-elk/src/export.mts
Comment thread .changeset/layout-elk-new-package.md
Comment thread examples/layout-elk-containers-ports-ts/src/example.ts Outdated
Comment thread packages/joint-layout-elk/README.md Outdated
…r-port ELK options

- Elements embedded in another element are laid out as a container -
  the parent is resized (and positioned) by ELK to fit its content, at
  any nesting depth. Cross-container links are filed under their
  lowest common ancestor per ELK's hierarchical-edge convention.
- Ports route edges to/from the position JointJS itself already
  computes for them (`elk.portConstraints: FIXED_POS`); the new
  `positionPorts` option lets ELK reposition/reorder them instead.
- Add a `portOptions` callback for per-port ELK layout options,
  alongside the existing `nodeOptions`/`edgeOptions`.
- Default to `elk.hierarchyHandling: INCLUDE_CHILDREN` so edges
  crossing a container's boundary are accounted for during layout.
- Migrate the `layout-elk-ts` example to the package's `layout()` entry
  point instead of hand-rolled ELK export/import.
A fixed (non-random) system diagram exercising the new @joint/layout-elk
capabilities: three containers, eight services connected through ports
(three of which opt into `positionPorts` so ELK orders them to minimize
crossings), and `elk.aspectRatio`/`elk.layered.wrapping.strategy` tuned
to keep the drawing within a soft 1000-unit width budget.
The package itself (README, build config, initial layout()) isn't
covered by an earlier changeset - this PR is its first appearance on
master, so the changelog should read as an introduction, not a bump.
Geliogabalus and others added 25 commits October 8, 2026 16:54
… the ELK result

exportLinkLabel can drop labels, so an ELK label's index no longer matched
the link label it was made for. Each ELK label now carries an id with its
link label index, which importLayout reads back.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…raints by default

Without port constraints ELK moved ports to another side or reordered them,
contrary to the documented behavior of keeping ports where JointJS places them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
An aborted layout() rejects with the signal's reason and applies nothing to
the graph. The default worker is now driven by a small client for ELK's own
worker script instead of elk-api.js, which can neither cancel a layout nor
settle one whose worker is terminated: a layout the worker is busy with is
stopped by terminating it, and a new worker takes over the layouts still
waiting. ELK on the main thread or a custom instance can't be stopped, so only
its result is ignored.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… main thread

elk.bundled.js was imported statically, so every app shipped ELK twice - in
the main bundle and in the worker - even though the main-thread copy is only
a fallback. It is now imported dynamically, so bundlers split it into a chunk
of its own (in webpack, the example's main bundle shrinks from 3.5 MB to
1.9 MB). The UMD build, which can't load chunks, still imports it statically.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ils to load

Any worker error used to send the layout in progress to the main thread for
good - including a crash during the layout (e.g. out of memory), which the
same graph would then repeat on the main thread, freezing or crashing the
page. The worker now counts as loaded once it answers its first message: an
error before that still falls back to the main thread, a crash after it
rejects the layout it was busy with, and a new worker takes over the rest.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A worker that can't be started or fails to load used to fall back to the
main thread silently - layouts kept working, only blocking the page, so a
misconfigured bundler went unnoticed. It is now reported once with a
console.warn. The README lists the common causes, among them the Vite dev
server, whose dependency pre-bundling breaks the worker file's URL.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… instance runs

'auto' (default) keeps running ELK in the Web Worker where one can be used and
on the main thread otherwise. 'worker' rejects instead of falling back to the
main thread, so a large layout never blocks the page. 'main' runs ELK on the
main thread without starting a worker, e.g. for tests or debugging.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The new package's `minor` changeset releases it as 4.4.0, alongside @joint/core.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- flowchart: abort a layout still running when a new one starts, so only the
  latest is applied and an earlier one no longer unfreezes the paper too
  soon; keep the zoom level when refitting after a layout; lay out again
  only after a drop that actually reorders; drop the redundant
  exportLinkLabel and the comment describing elk.port.index code that
  doesn't exist; explain the INTERACTIVE layering strategy
- containers-ports: drop the edge-only elk.layered.priority.direction set on
  the root, the unused import and the stale ELK_MAX_WIDTH reference
- rectpacking: keep re-running a layout requested while one fails
- layout-elk-ts: unfreeze the paper when the layout fails; describe the demo
  in its README
- default: don't repeat the package's default algorithm
- webpack: 'auto' publicPath, so the ELK worker and main-thread ELK chunks
  load from wherever the demo is served (the dev server keeps /dist/), and
  anchor the .m?js rule

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…orkerElk()

The package no longer starts a Web Worker of its own. Locating the worker
script depends on the bundler (Vite's dev server, esbuild, an absolute
publicPath, the UMD build and CSP all needed workarounds), so starting it is
now up to the app, which knows its bundler:

- Without an `elk` option, layout() runs ELK on the main thread - it works
  anywhere with no setup. The main-thread ELK chunk is still loaded lazily.
- createWorkerElk(createWorker) returns an ELK instance running in a worker
  the app starts, e.g. new Worker(new URL('@joint/layout-elk/worker',
  import.meta.url), { type: 'module' }) - tested with webpack 5 and Vite (dev
  and build). @joint/layout-elk/worker is a new subpath export, so elkjs
  resolves from this package rather than from the app.
- The instance keeps the worker client's behavior - an aborted layout stops
  the worker busy with it, a crash rejects only the layout it was running -
  but a worker that fails to load now rejects its layouts instead of falling
  back to the main thread. terminate() stops it.
- The `thread` option and the fallback warning are removed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Every ELK example except the default one now starts a worker of its own
(@joint/layout-elk/worker) and passes it to layout() - the flowchart's
superseded layouts now stop the worker busy with them. The default example
keeps layout() at its simplest, on the main thread. The examples' TypeScript
module target is raised to ES2020 for import.meta.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
createWorkerElk()'s client took the first unsettled layout to be the one
the worker was busy with. Two cases broke that:

- A layout aborted while waiting its turn is still laid out by the worker -
  a crash during it was blamed on the next layout, which never ran.
- A layout whose graph can't be posted (e.g. DataCloneError for a function an
  export callback left in it) was rejected but left queued - an abort then no
  longer terminated the busy worker, and a restart stopped re-posting at it,
  leaving the layouts behind it pending forever.

The client now keeps the ids posted to the current worker in order, and
rejects a layout that can't be posted instead of queuing it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ExportPortLabelCallback is typed to return `false` to drop the label, and
the README promises that for every export callback, but its return value was
ignored.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…as it is

`??` replaced a `null` reason with a new AbortError - the default is now
only made up where the browser doesn't support `AbortSignal.reason`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…opt), and PortLabelPositionType

Re-applies the port typing fixes from the port-label-position-type and
port-prop-overloads changesets on top of master's dia.d.ts.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…omputedLabels()

@joint/core made dia.Link#getComputedLabels() protected in its types (clientIO#3528).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Geliogabalus and others added 4 commits October 8, 2026 17:22
… exported embeds as a leaf

- Element, port, link and label ids went into the ELK graph as they were:
  element `a`'s port `p` and element `a:p` shared the ELK id `a:p`, and an
  element could share the root's id `root`. Cell and port ids are now
  escaped (`\` and `:`), so nodes, ports, labels and the root (now `::root`)
  can't collide - ordinary ids stay as they are.
- A container whose embeds exportElement all dropped went to ELK as a 0x0
  container, so ELK made no room for it. It is now laid out as a leaf, with
  its own size.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…inline` override

Nothing reads a link label's `inline` property any more - the package places
every link label inline by default.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The UMD build keeps elkjs external and runs ELK through the `ELK` global -
the README said it needed no setup, and that main-thread ELK is always
imported dynamically.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants