Diagram - Layout Execution and Ownership
Run and cancel explicitly, own worker delivery, reject stale input and keep layout changes in one Undo unit.
#Usage
The default executor is cooperative main-thread work. Supply an application-owned worker executor only when its worker asset is configured. Expose layeredDiagramLayout as a component field for the template. The runnable examples supply the graph and worker asset setup.
This skeleton assumes an initialized store with page page. Use First Diagram for model creation, style imports and store disposal.
Start with the default cooperative executor used by the family demos. Add a worker when your application's graph and responsiveness requirements justify its asset/deployment setup. The provider computes positions; the controller owns input capture, stale-result rejection, validation and one transaction.
Import DiagramLayout and the provider for this family from @primeui/vue-diagram/layout. Place the component inside Diagram.Root, bind options with :diagram-layout and the provider with :provider. Use the layout setup for the component composition. The complete examples below supply the model, options and cleanup.
#Run locally and read state
DiagramLayout is a child component of Diagram.Root. Pass the options object through :diagram-layout and the actual provider instance through :provider. The hierarchical example shows local execution and optional copied settings.
| Component member | Default / contract |
|---|---|
diagramLayout | {}; immutable captured options for each job. Replace the options ref value when settings change. |
provider | layeredDiagramLayout; an object implementing the provider contract. |
executor | executeDiagramLayout; cooperative main-thread execution. |
state | Initially { status: 'idle', diagnostics: [] }; readonly state from its owned controller. |
run() | Starts explicitly; returns Promise<DiagramLayoutState>, or undefined before the owner effect initializes. |
cancel() | Cancels the currently owned computation; no-op if no active job. |
executeDiagramLayout pulls the provider's generator inside posted callbacks.
It checks cancellation and yields between provider steps. A long
iterator.next() can keep the host busy before the next yield. Cooperative
scheduling does not provide preemption or a hard latency guarantee.
| State / field | What to show or do |
|---|---|
idle | Ready; no job started. |
running | Keep Cancel available; optional progress has phase, completed, total. |
committed | Positions accepted in one model transaction. Fit afterward if useful. |
noop | Valid result with no changed positions; no new Undo entry. |
pending | Controlled-store proposal awaiting host acceptance; it is not yet committed. |
cancelled | Owned job ended; diagnostics distinguish explicit cancellation from stale input. |
failed | Input, provider, executor or result validation failed; show the diagnostic and retain the graph. |
requestId, diagnostics, transaction | Optional request/transaction details plus always-present diagnostics. Do not infer success merely from a resolved promise. |
Progress phases are cycles, ranks, crossings, coordinates and forces.
Totals describe the current phase, not a monotonic whole-job percentage.
Diagnostic codes are unsupported, invalid-input, budget, constraint,
stale, cancelled and execution. Read the message rather than treating every
failure as an out-of-memory or performance problem.
#Own a real worker asset
The complete demo includes a local createDiagramLayoutWorkerExecutor helper and calls it with one options object. Copy that helper with the demo; it is application code, not a package export. createWorker returns a new dedicated module worker for each job, using
the local workers/layout-worker.ts asset. Include this file in your application's
worker compilation and deploy the emitted asset with the application. Copy both
the demo's source and the complete supporting file below. A worker URL is
consumer-owned; it is not provided by the base Diagram styles or UI module.
#layout.worker.ts
import { runDiagramLayoutWorker, type DiagramLayoutWorkerRequest } from '@primeui/vue-diagram/layout-worker';
self.onmessage = (event: MessageEvent<DiagramLayoutWorkerRequest>) => {
runDiagramLayoutWorker(event.data, (message) => self.postMessage(message));
};Save the shown file as workers/layout-worker.ts alongside helpers/worker-executor.ts in the copied source structure. Keep the relative URL in that helper aligned with the worker filename and your worker build configuration.
| Worker options | Contract |
|---|---|
createWorker: () => Worker | Required; return a fresh dedicated worker. Application owns URL, bundler output, CSP and deployment. |
fallback?: DiagramLayoutExecutor | Omitted means failure stays failure. Pass executeDiagramLayout for explicit cooperative main-thread fallback. |
timeoutMs | Default 30,000; worker-response timeout. It does not establish a timeout for an independently supplied fallback executor. |
metrics | Optional callback with computeMs, maxStepMs, transportMs, mode: 'worker' | 'fallback'. Instrumentation is not a latency qualification. |
Use this local helper with the built-in providers: layered, tree, mind map, radial, force, bracket, family, structured, specialized and Venn. It sends the provider ID; the worker selects the corresponding built-in implementation. It does not transfer a custom provider’s create function. Use your own executor/protocol for custom providers or run their actual implementation locally.
The factory supports the exact built-in provider instances: layered, tree,
mind map, radial, force, bracket, family, structured, specialized and Venn.
A custom object sharing one of their IDs is rejected before a worker starts.
Use your own executor/worker protocol for custom providers, or configure a
fallback that can execute their actual create method.
The demo's normal path reports worker delivery or the configured fallback.
Its two simulated factory failures compare explicit fallback with failure
without fallback. They are labeled simulations; they do not prove your deployed
worker-src policy. Worker construction/load/clone/protocol errors and timeout
can invoke configured fallback. Cancellation terminates current worker work
and cancels an active fallback; it does not start a fallback just to continue
a cancelled job.
The transport clears listeners/timers and terminates the dedicated worker on success, failure, cancellation and timeout. Do not reuse a shared worker that another feature owns. On SSR no worker is created until an explicit browser Run action invokes this factory. An application that invokes it outside the browser must supply its own availability/fallback policy.
#Reject stale model and geometry results
This small custom row provider runs locally. The application executor holds result delivery for one second so stale ownership is observable without a huge graph or benchmark. Select Run, then Cancel, Edit model during job, or Change geometry during job during that window.
A model edit commits Alpha's new position and invalidates the old job. The geometry action injects a synthetic session measurement and changes no saved record. Both obsolete results are cancelled with a stale diagnostic. The held row cannot overwrite newer model or geometry work. Restart provides a clean fixture after either scenario; the deliberate delay is demonstration transport, not a performance measurement.
Preparation captures an immutable version-1 input with request ID, model revision, geometry revision, page, options, measured units and edges. Store commits/previews, geometry changes and policy changes invalidate active work. Provider/options changes cancel it as stale; replacing store/session/executor replaces and disposes the component's controller. A result is checked again against current revisions, previews and policy before applying.
Stale failure preserves the user's newer graph; it does not roll that edit back to the old layout snapshot. If a layout has already submitted a controlled proposal, cancelling computation does not cancel the host-owned proposal. See Controlled State for host decisions.
#Implement a provider or executor
The custom demo exposes the contracts directly, without hiding them behind a new application service:
| Contract | Required behavior |
|---|---|
DiagramLayoutProvider | id, capabilities, create(input): Generator<DiagramLayoutProgress, DiagramLayoutResult, void>. No store writes or implicit browser scheduling. |
DiagramLayoutCapabilities | directions, cycles, disconnected, compounds: 'rigid-scoped', measuredBodies, portsAndLabels, pinned, incremental. Describe the actual supported profile. |
DiagramLayoutInput | Version 1, matching requestId, model/geometry revisions, page ID, options, units with position/footprint/fixed status, edges with endpoints/port order/label extents, optional containment and Venn body descriptors. |
DiagramLayoutResult | Version 1, matching request ID, one finite { id, x, y } for every unit, diagnostic list and valid unique reversedEdges. Diagnostics cause the controller to reject the result. |
DiagramLayoutExecutor | (provider, input, progress) => { result: Promise<DiagramLayoutResult>, cancel(): void }. Own scheduling, result delivery and resource release. |
prepareDiagramLayout(store, session, options, requestId) | Captures measured scope input and validates preparation prerequisites; may throw. Does not commit positions. |
DiagramLayoutController(store, session, executor) | Owned observer/controller; start(options = {}, provider = layeredDiagramLayout), cancel(message?, code?), getSnapshot(), subscribe(listener), dispose(). Subscription returns a disposer. |
The example provider sorts IDs and places measured footprints in a single row. It declares only Right direction, no pins and no incremental support. It is not a general constraint solver. The controller still rejects moved fixed units, nonfinite/incomplete/duplicate positions, ordinary overlaps, invalid edge metadata, containment violations and currently forbidden edits. Custom results do not gain Venn's trusted overlap exception by choosing a public ID or capabilities.
The delayed executor forwards provider progress and clears its timer on cancel. It also cancels underlying cooperative execution. Apply the same ownership to any custom worker, stream or transport: settle/cancel owned promises, release listeners and prevent late delivery from applying.
#One-step Undo and lifecycle
A valid managed result is one transaction containing every changed position.
Undo once restores all of them. A noop adds no history. On a controlled store,
host acceptance commits the proposal; rejection does not add committed history.
Result delivery alone is not acceptance. Connector routing remains separate.
Keep store/session/controller stable for normal Run, Cancel, settings and Undo.
Restart explicitly cancels work and disposes the old store. DiagramLayout releases its controller and subscriptions on replacement or unmount; the root releases its session. Direct controller consumers must unsubscribe and call dispose()
when their owner ends. No demo autoruns expensive work.
#Capacity and performance
Use the chooser's source caps and each family's topology/geometry restrictions to reject unsupported input before promising a result. Preparation has a 10,000-unit / 40,000-edge ceiling; many providers have smaller caps and additional work/search budgets. Final coordinates are bounded to one billion. Result validation and conservative footprint checks also consume time on the host.
Passing provider examples, resource caps, worker transport or bounded work checks does not establish measured production layout/routing or ordinary-edit latency. Cooperative scheduling yields between provider steps. A step, validation, geometry capture, transport or commit can still keep the host busy before the next yield. These focused demos establish usage and ownership, not enterprise capacity or benchmark guarantees. Choose application limits from representative production graphs and deployment evidence.
Choose a layout · History · Permissions