Release Next v3.0.0-next.12
-
#803 by @bobsingor – The default stamp libraries are now self-describing, Acrobat-compatible PDFs. Each
<locale>/stamps.pdfcarries its name as/Title, registers every page in/Names /Pagesasidentifier=label(Approved=Goedgekeurd), and records the library id (embedpdf-standard), the locale, and each stamp's kind in/PieceInfo. Import one withimportLibraryPdffrom@embedpdf/plugin-stampand the title, identifiers, and labels come from the file; drop the same file into Acrobat's Stamps folder and it appears there. The artwork is unchanged from the previous release (every page renders pixel-identical). Locales: en, de, nl, fr, es, zh-CN, sv, ja. A new@embedpdf/default-stamps/libraryentry delivers each locale through the module graph:loadDefaultLibrary(locale)resolves to the library's bytes from a generated, lazily imported module, so the library ships as a chunk of your own build with nothing to copy and no CDN.LOCALESlists the shipped codes.The legacy
<locale>/manifest.jsonfiles remain available for existing v2 viewers that load this package from an unversioned CDN URL. They preserve v2's library and stamp ids, categories, labels, and page indexes, and point to the same adjacentstamps.pdffiles. V3 reads the PDF metadata directly and does not load these manifests.
-
#803 by @bobsingor – Add
doc.annotations.flattenanddoc.annotations.exportAppearanceto the public contract.Add
doc.pages.setNameanddoc.pages.removeNamefor registering, renaming, and removing named pages (/Names /Pagesentries) on a layer.
-
#803 by @bobsingor – The cloud engine implements
page(pon).annotations.flattenoverPOST …/annotations/pages/{pon}/items/flatten(content and annotation planes patched in place,annotations.flattenedpublished) andpage(pon).annotations.exportAppearanceoverPOST …/items/appearance(PDF bytes; a read).It also implements
pages.setNameandpages.removeNameoverPOST …/pages/namesandPOST …/pages/names/delete. Both advance onlydocVersionandlayoutVersion, so the cached manifest is patched in place, per-page render/text/annotation leaves stay valid, and the result carries the fresh layout includingnamedPages.
- #803 by @bobsingor – Add typed SDK methods for selective annotation flattening and appearance export through
client.doc.annotations.flattenandclient.doc.annotations.exportAppearance. Addclient.doc.pages.setNameandclient.doc.pages.removeNamefor registering, renaming, and removing named pages.
-
#803 by @bobsingor – Add
POST /v1/docs/{docId}/layers/{layerName}/annotations/pages/{pon}/items/flatten(flatten a chosen set of the page's annotations; gated like page flatten and persisted the same way — one page's content and annotation versions advance) andPOST …/items/appearance(the chosen annotations' appearances as one single-page PDF; gated bydoc.download, no-store).Add
POST /v1/docs/{docId}/layers/{layerName}/pages/namesandPOST …/pages/names/deleteto register, rename, or remove a/Names /Pagesentry on a layer. Both are page-structure mutations gated bydoc.pages.assemble: the worker writes a new layer artifact and the doc and layout versions advance, exactly like a page move, with no new resource, version, or cache scope./layoutresponses now includenamedPages.
- #803 by @bobsingor –
PluginContext.tryForDocument(token, documentId)—forDocumentfor an OPTIONAL dependency: null when the plugin is not installed or that document is not ready, never a throw (a workspace plugin reaching a sibling's document-scoped lens without requiring it).
-
#803 by @bobsingor – Selective annotation flatten and appearance export.
page(pon).annotations.flatten(refs, usage?)ispages.flattenfor a chosen set: painted annotations are removed from the page, ineligible ones (hidden for the usage, popups, no appearance) stay and reportskipped, and the result (AnnotationFlattenResult) carries that page's new pins plus anannotations.flattenedevent.page(pon).annotations.exportAppearance(refs)returns the chosen annotations' normal appearances as one single-page PDF sized to their union rect — vector, positions preserved, the source untouched; all-or-nothing. Both are optional service members;runAnnotationFlattenConformanceandrunAnnotationAppearanceExportConformancelock the shared behavior.Named pages join the page list.
PageListSnapshot.namedPagescarries the catalog's/Names /Pagesand/Names /Templatesregistrations in tree order —NamedPageEntryis the decoded key plus a target classified aspage(a page inpages),template(a hidden/Type /Templatepage that is never listed or rendered), ordangling. Because a registration only means something against the page set that contains its target, it is layout data likelabeland shares the layout version rather than a plane of its own.DocumentPagesServicegains two optional page-structure mutations:setName({ name, pageObjectNumber, replace? })registers a key, replaces what an existing key points at, or renames in one job;removeName({ name })drops a registration and keeps the page. Both return the fresh layout asPageNameResult(layout plus the docVersion/layoutVersion pins) and publish apages.namedevent. Deleting a page removes every registration pointing at it.runNamedPagesConformancelocks these invariants for every engine. The engine never interprets key text.Stamp
/Nameaccepts any non-empty name — a standard stamp name or a custom identifier such as an Acrobat library's#…key — and a stamp patch may clear it withname: null.
-
#803 by @bobsingor – The local engine implements
page(pon).annotations.flatten(gated likepages.flatten, publishesannotations.flattened) andpage(pon).annotations.exportAppearance(gated bydoc.downloadlikepages.extract).It also implements
pages.setNameandpages.removeName: register, rename, or remove a/Names /Pagesentry as a page-structure mutation gated bydoc.pages.assemble, with the fresh layout returned and apages.namedevent published.pages.list()includesnamedPages.The local engine never contacts a CDN. The zero-config default is the
embedpdf.wasmyour bundler emits beside your code (@embedpdf/engine-runtime-wasm32/wasm-url, which webpack, Vite, Rspack, Parcel, and Turbopack all resolve), and it is now streamed and compiled by the worker as it downloads — the previous fetch-then-fallback path had given up streaming. When a toolchain cannot carry that asset (Angular's application builder, plain esbuild), boot fails with the fix named instead of silently fetching a possibly mismatched binary from jsDelivr:DEFAULT_WASM_URLand the fetch-failure fallback are gone.New
@embedpdf/engine/portable: the samelocalEngine()with the wasm delivered through the module graph — a lazy chunk of your own build, gzipped and inflated in the browser — so it works with every bundler at the same cost over the wire, with no asset to copy.wasmLoaderjoinswasmUrl,assetsUrl, andwasmBinaryas an explicit source (bytes produced on demand at boot). Explicit sources never fall back.Angular needs neither: the package's export map routes the
es2020condition Angular's application builder resolves with to the portable build, so the plain@embedpdf/engineimport is zero-config under Angular too. Other bundlers do not declare that condition and keep the streamed asset.
-
#803 by @bobsingor –
EPDFPage_FlattenAnnotationsflattens a chosen set of a page's annotations with a per-entry status (applied, skipped, not-on-page), replacing the single-annotationEPDFAnnot_Flatten.EPDFPage_ExportAnnotationsAsDocumentflattens a set's appearances into a new single-page document sized to their union rect, replacingEPDFAnnot_ExportAppearanceAsDocumentandEPDFAnnot_ExportMultipleAppearancesAsDocument, which mishandled rotated appearances and the rect fit. Whole-page flatten, selective flatten, and export now share one candidate plan and one placement writer (ISO 32000-2 12.5.5 fit,/Matrixhonored, no content re-parsing); resources shared between exported appearances are cloned once.New named-page functions:
EPDFDoc_GetNamedPageCount,EPDFDoc_GetNamedPageAt(key as UTF-16 plus the value's object number and kind: page, template, or dangling),EPDFDoc_SetNamedPage(create or replace, pages in the page tree only),EPDFDoc_RemoveNamedPage, andEPDFDoc_RemoveNamedPagesForPage.EPDFDoc_DeletePageByObjectNumbernow removes the/Names /Pagesregistrations of the page it deletes. The name-tree index search reports a pair whose value is a missing object (with a null value) instead of hiding it and desynchronizing later indices.Annotation
/Nameis text:EPDFAnnot_SetNametakes any name, writes a name object (escaping applied by the serializer), and never touches/AP;EPDFAnnot_GetNamefills a text buffer. TheFPDF_ANNOT_NAMEenum, its subtype validation, and the sentinel that removed/Nametogether with/APare removed — remove/NamewithEPDFAnnot_RemoveKey(annot, "Name").
- #803 by @bobsingor – New
@embedpdf/engine-runtime-wasm32/wasm-inline:embedpdf.wasmas a gzipped, base64 ES module (about 3.6 MB on disk, the same bytes as the file over the wire), generated in the same build step as the binary. This is what@embedpdf/engine/portableimports lazily so the wasm can travel through the module graph where a bundler cannot emit it as an asset.
-
#803 by @bobsingor – New
annotations.flattenandannotations.exportAppearanceworker jobs over the runtime's set-based flatten: resolve refs on the page, hand the set to one candidate plan, and either paint in place (bumping that page's revision and weak-annotation state like a page flatten) or into a scratch document returned as PDF bytes.Every page list now includes the catalog's named-page registrations (
namedPages), read from the runtime with each value classified as page, template, or dangling. Newpages.setNameandpages.removeNameworker jobs register, rename, or remove/Names /Pagesentries and return the fresh layout; page deletion drops the registrations that pointed at the page.Annotation
/Nameis written and read as text: note and file-attachment icons map their ids to PDF names, stamps accept any non-empty name (standard or custom) and report custom names verbatim instead of collapsing them, and a stamp patch withname: nullremoves the entry without touching the appearance.
- #803 by @bobsingor – The stamp hover ghost is rendered at the on-screen device pixel size and re-requested when the zoom crosses a size bucket, so large vector stamps stay sharp.
@embedpdf/react/stampadditionally re-exportsindexedDbByteStoreandByteStorefrom@embedpdf/web, the browser store forpersistStampLibraries/restoreStampLibraries.
- #803 by @bobsingor –
indexedDbByteStore(dbName, { storeName? })is the browser's bytes-by-id store: one IndexedDB object store withlist,put, anddelete. It is the adapter for any plugin's DOM-free persistence port (structurallyStampLibraryStorefrom@embedpdf/plugin-stamp), written once here so every framework binding shares it.
-
#803 by @bobsingor – Stamps can be placed without the pointer and ghosted at any zoom.
placeStamp(input, placement)creates a stamp annotation by code — the same validation, fit, page clamp,/Name, and/Subja click afterarmStampproduces — and resolves to the new annotation's ref;StampPlacementnames the page, the anchor point, an optional width, and rotation.StampToolInputgainsname(the placed/Name) andsubject(the placed/Subj), and itspreviewnow also accepts aStampPreviewProvider: a function the hover ghost asks for a render at the device pixel width it is displayed at. Requests are bucketed to powers of two (previewBucket) and cached per bucket for the arm's lifetime, so a zoom gesture never renders per frame. The host capability'sarmedStampPreview(devicePixelWidth?)is now asynchronous and takes that width.armStampno longer clears the payload it just set when activating the built-in stamp tool from another active tool. Armed stamps now place correctly instead of falling through to the tool's source callback, while the legacyannotation-stamptool tag remains supported.
-
#803 by @bobsingor – The Insert tab's Stamp action now opens the stamps sidebar instead of a click-then-pick file dialog; the Image action handles arbitrary PNG and JPEG insertion. The sidebar is now the classic picker over real libraries. Its built-in library comes from
@embedpdf/default-stamps— the standard rubber stamps as one Acrobat-compatible PDF per locale — loaded on the panel's first open (never at boot), in the locale negotiated from the viewer's language and the browser's, and swapped when the viewer's locale changes; the canvas-drawn placeholder set is gone. The panel gets a library dropdown ("All stamps" plus one entry per library, shown once there are two), a two-column thumbnail grid with the label as tooltip, a hover×that removes a stamp from a user library, and per-library export as PDF and remove. Custom libraries persist in IndexedDB across reloads."Make stamp" joins the annotation selection strip: with one or more annotations selected on a page, it turns their appearances into a vector stamp in a "My stamps" library and opens the panel on it. Widgets and pending redaction marks are excluded.
New
stampsoption on the viewer customization:stamps: { defaultLibrary: false }ships no built-in library and makes no request (air-gapped);stamps: { defaultLibrary: 'https://your.cdn/{locale}/stamps.pdf' }self-hosts a copy of@embedpdf/default-stamps. The default is the copy that ships with the viewer, loaded as a lazy chunk of your own build; nothing is ever fetched from a third party. English and Spanish strings updated.
- #803 by @bobsingor – The
cloudpdf.jsartifact carries the built-in stamp library as lazy sibling chunks in its own folder instead of inlining eight locale PDFs as base64. Nothing is fetched from a third party; setstamps.defaultLibraryto self-host or disable it.
-
#803 by @bobsingor – The built-in stamp library ships inside the viewer instead of being fetched from a CDN. The npm entry keeps
@embedpdf/default-stamps/libraryexternal, so its locale modules become lazy chunks of your own build; the CDN snippet carries them as sibling chunks in its folder.@embedpdf/default-stampsis now a dependency. Previously a library build could inline the eight PDFs into the JS chunk as base64, a form webpack rejects.The viewer registers
stampPlugin(), exposesStampTokenthrough its drive door, and routes the Insert tab's Stamp action to the stamps sidebar. Arbitrary PNG and JPEG insertion remains available through the Insert tab's Image action.The CDN snippet (
dist/embedpdf.js) once again findsembedpdf.wasmwhen loaded from another origin: the wasm is now an asset Vite emits into the dist folder and references by a URL relative to whichever chunk needs it, instead of a path guessed against the entry file — which had resolved tochunks/embedpdf.wasmand failed. A cross-origin test (pnpm testin the viewer package, Playwright against the built artifact) now guards it: the snippet must render with every sibling fetched from its own folder and no request to any other origin.
@mermaid-js/layout-tidy-tree@1.0.0
-
#8213
33442fdThanks @aloisklink! - chore!: require ES2024, Safari 17.4+, Node.JS v22.12+Mermaid is now built to target Safari 17.4+ and ES2024. If you need to support older browsers, you may need to polyfill or transpile mermaid.
Safari 17.4+ has been chosen as the floor, as unlike Firefox/Chrome, older iOS devices don't get major Safari updates.
Node.JS v22.12+ is also declared as requirement in our
package.jsonfiles, but as mermaid requires a browser, this is mainly so we can use dependencies that also declare a Node.JS v22.12+ requirement, without causing issues for users when runningnpm install.
- Updated dependencies [
7a3c1a8,0cf3797,846fd65,33442fd,dc2e453,ce0302d,ad98070,e2aab3f,6b79518,30325d4,cd48a64,c3ee3c7,1ee934d,a7831c5,0cf3797,0320406,810893c,e36b883,b993915,4e00c5c,3f05015,d57ed55,1fc5bb3,b979eb4,7ee4c3f,a6ec7ff,1246a55,813c766,8603bdd,43d9fbc,2878cf3,16b9a7d,def4c81,3802472,a19bd08,f5af413,a3a92ba,e691042,ba0deed,b05824a,0ec29e9,0d77926,93edd72,75e6c30,6df1149,a31ecb7]:- mermaid@12.0.0
@mermaid-js/examples@2.0.0
-
#8213
33442fdThanks @aloisklink! - chore!: require ES2024, Safari 17.4+, Node.JS v22.12+Mermaid is now built to target Safari 17.4+ and ES2024. If you need to support older browsers, you may need to polyfill or transpile mermaid.
Safari 17.4+ has been chosen as the floor, as unlike Firefox/Chrome, older iOS devices don't get major Safari updates.
Node.JS v22.12+ is also declared as requirement in our
package.jsonfiles, but as mermaid requires a browser, this is mainly so we can use dependencies that also declare a Node.JS v22.12+ requirement, without causing issues for users when runningnpm install.
@mermaid-js/mermaid-zenuml@1.0.0
-
#8213
33442fdThanks @aloisklink! - chore!: require ES2024, Safari 17.4+, Node.JS v22.12+Mermaid is now built to target Safari 17.4+ and ES2024. If you need to support older browsers, you may need to polyfill or transpile mermaid.
Safari 17.4+ has been chosen as the floor, as unlike Firefox/Chrome, older iOS devices don't get major Safari updates.
Node.JS v22.12+ is also declared as requirement in our
package.jsonfiles, but as mermaid requires a browser, this is mainly so we can use dependencies that also declare a Node.JS v22.12+ requirement, without causing issues for users when runningnpm install.
- Updated dependencies [
7a3c1a8,0cf3797,846fd65,33442fd,dc2e453,ce0302d,ad98070,e2aab3f,6b79518,30325d4,cd48a64,c3ee3c7,1ee934d,a7831c5,0cf3797,0320406,810893c,e36b883,b993915,4e00c5c,3f05015,d57ed55,1fc5bb3,b979eb4,7ee4c3f,a6ec7ff,1246a55,813c766,8603bdd,43d9fbc,2878cf3,16b9a7d,def4c81,3802472,a19bd08,f5af413,a3a92ba,e691042,ba0deed,b05824a,0ec29e9,0d77926,93edd72,75e6c30,6df1149,a31ecb7]:- mermaid@12.0.0
mermaid@12.0.0
Mermaid 12.0.0 ships ELK as the bundled default layout engine, a new redux-color / neo default appearance, and two new diagram types: UML use case diagrams.
This is a breaking release (ES2024, Safari 17.4+, Node 22.12+). Existing flowcharts, state and class diagrams will re-lay out and recolour; add layout: dagre, theme: default and look: classic to your config to keep the old look. Full changelog below.
-
#8213
33442fdThanks @aloisklink! - chore!: require ES2024, Safari 17.4+, Node.JS v22.12+Mermaid is now built to target Safari 17.4+ and ES2024. If you need to support older browsers, you may need to polyfill or transpile mermaid.
Safari 17.4+ has been chosen as the floor, as unlike Firefox/Chrome, older iOS devices don't get major Safari updates.
Node.JS v22.12+ is also declared as requirement in our
package.jsonfiles, but as mermaid requires a browser, this is mainly so we can use dependencies that also declare a Node.JS v22.12+ requirement, without causing issues for users when runningnpm install. -
#8155
810893cThanks @ashishjain0512! - feat!: ELK is now bundled with mermaid and is the default layout algorithm.ELK previously shipped as a separate
@mermaid-js/layout-elkpackage that sites had to install and register. It is now part of mermaid itself and registered automatically, solayout: elk— and theelk.stress,elk.force,elk.mrtree,elk.sporeOverlap,elk.boxandelk.rectpackingvariants — work with no setup.This changes how existing diagrams look. Flowchart, state, class, entity-relationship, requirement, use-case diagrams that do not specify a
layoutare now laid out by ELK instead of dagre. To keep the previous layout, set dagre explicitly:--- config: layout: dagre ---
or globally,
mermaid.initialize({ layout: 'dagre' }).Mindmap is unchanged: it keeps laying out with cose-bilkent unless a layout is explicitly requested (in the tiny build, which ships neither ELK nor cose-bilkent, it falls back to dagre).
Other notes:
- ELK is loaded as a separate chunk in the ESM builds, so it is only fetched when a diagram actually uses it. The single-file IIFE build (
mermaid.min.js) inlines it and grows by roughly 500 kB gzipped. - The tiny build deliberately omits ELK to stay small, and falls back to dagre for diagrams that ask for an ELK layout. Its size is unchanged.
@mermaid-js/layout-elkis no longer needed on normal builds — existingmermaid.registerLayoutLoaders(elkLayouts)calls keep working and can be removed. It is still published, and remains the way to add ELK to the tiny build.dist/mermaid.esm.min.mjsnow contains syntax thates-module-lexer(used by Vite) rejects withcontent contains invalid JS syntax. Bundler users are unaffected as long as they import themermaidpackage specifier, which resolves to the core build; only builds that point Vite directly at that dist file need to switch to the package specifier or the core build.- State diagrams now resolve their layout through the same registration check as every other diagram, so an unavailable layout falls back instead of failing to render.
An unrecognised
themename now resolves to the default theme in name as well as in variables; previously the invalid name stayed in place while the default's variables were loaded, and every palette-aware stylesheet gates on the name.theme: 'null', the documented way to disable the pre-defined themes, is unaffected. Note thatneopaints node strokes with a gradient when the theme setsuseGradient, whichbasedoes; setting a customnodeBorderonbaseturns the gradient off. - ELK is loaded as a separate chunk in the ESM builds, so it is only fetched when a diagram actually uses it. The single-file IIFE build (
-
#8211
a19bd08Thanks @ashishjain0512! - Removed: thedefaultRendereroption of theflowchart,classandstateconfig sections.Use the top-level
layoutoption instead:--- config: layout: dagre # or elk ---
defaultRenderernamed a rendering engine back when there was more than one. There is not. All of its accepted values —dagre,dagre-wrapper,dagre-d3— had come to select the same unified renderer, and itselkvalue did nothing but setlayout: elkon your behalf. The option's documented purpose no longer existed.What it still did was stranger than doing nothing. The detectors branched on whether the value was recognised, so a valid value routed a diagram to the modern renderer and an invalid one silently routed it to the legacy renderer. Since the default was always a valid value, the legacy
classandstaterenderers were unreachable except by misconfiguring the option.layoutselects the layout algorithm, as it already did. Nothing else does.graph,classDiagramandstateDiagramalways render with the unified diagram. This was already true for every valid configuration.- The legacy
flowchart,classandstatediagram ids are gone.flowchart-v2,classDiagramandstateDiagramare whatdetectTypenow returns for that syntax. flowchart-elkas an explicit diagram keyword still works.
Configuration that still sets
defaultRendereris ignored rather than rejected, so nothing throws — but it no longer has any effect, and diagrams that relied onflowchart: { defaultRenderer: 'elk' }to get an ELK layout should setlayout: elk. -
#8223
6df1149Thanks @aloisklink! - refactor!: remove layout internal exportsRemove the
clearLayoutRenderState,createCommonLayoutRenderer,defaultMeasureLayout,paintLayoutDatafunctions, and theCommonLayout*types from Mermaid's public API.These functions were only used by the
@mermaid-js/layout-elkpackage, which now bundles them. Removing them makes it easier to avoid major versions of mermaid.
-
#8048
7a3c1a8Thanks @knsv-bot! - feat: add UML use case diagrams with actors, use cases, system boundaries, relationships, stereotypes, notes, JSON tables, class and style support, accessibility metadata, and business variants. -
#8073
cd48a64Thanks @knsv-bot! - feat:defaultMeasureLayoutaccepts aunwrapGroupLabelsoption so a layout engine can ask for cluster labels to be measured at their natural width instead of theflowchart.wrappingWidthfallback.insertClusterpaints a plain cluster label with an infinite width while the measurement pass wrapped it at 200px, so a layout that sizes compound nodes from the measured label sized them too narrow. Layouts now opt in explicitly; core no longer inspects the layout's name. Markdown cluster labels stay wrapped, since those are painted wrapped.The bundled ELK layout opts in, which changes the size of labelled subgraphs in ELK-laid-out diagrams. Diagrams laid out with dagre are unaffected.
-
#8073
0cf3797Thanks @knsv-bot! - feat: let a container pick its own ELK algorithm with@{ algorithm: … }, and addelk.boxandelk.rectpackingto the selectablelayoutvalues.A subgraph carrying
@{ algorithm: elk.box }is laid out with that algorithm in its own coordinate system instead of inheriting the diagram's. Supported values areelk.layered,elk.box,elk.rectpacking,elk.stress,elk.force,elk.mrtree,elk.radial, andelk.sporeOverlap; anything else is ignored with a warning rather than handed to ELK, where an unknown id would abort the layout. Containers with edges crossing their boundary fall back to the inherited algorithm, since isolated layout and cross-boundary edges are incompatible.Also in this release:
- ELK cluster labels are measured unwrapped, so a compound node is sized to fit its label instead of to a 200px wrapping width. This changes the size — and therefore the layout — of existing ELK diagrams that have labelled subgraphs.
elk.boxandelk.rectpackingplace nodes but never route edges. Edges they leave unrouted now fall back to a straight line between the two node centres instead of failing the render.keepEntryNodeOnToppins a cycle's true entry rather than the first-declared node when a back-edge feeds the entry, so recursive flows read from where they actually start.
-
#8193
813c766Thanks @ashishjain0512! -theme,lookandlayoutcan now be set per diagram type, in each diagram's config section —mermaid.initialize({ look: 'classic', flowchart: { look: 'handDrawn' } }), or the same underconfigin front matter. The schema uses the same mechanism to give a diagram type its own default. Resolution, highest first: front matter or directive,initialize(), the diagram type's default, the global default; a diagram-scoped value beats a global one set in the same layer.Swimlanes take
layout: swimlanefrom that schema default instead of having it forced by theirinithook, soswimlane: { layout: ... }and alayoutin front matter are now honoured. An unregistered layout also always falls back todagrewith a warning rather than throwing — state diagrams skipped that fallback, and mindmaps threw whencose-bilkentwas absent, as it is in@mermaid-js/tiny. -
#8147
43d9fbcThanks @ashishjain0512! - feat(themes): class boxes and flowchart subgraph containers now take a per-item colour under theredux-colorandredux-dark-colorthemes, cycling every 12 as ER entities already do. Collapsed subgraphs keep the slot they would have had expanded; nodes inside a subgraph stay uniform, and explicitclassDeforstylestill wins over the palette. -
#8191
2878cf3Thanks @ashishjain0512! - feat(themes): composite states now take a per-container colour under theredux-colorandredux-dark-colorthemes, as flowchart subgraphs already do. Each composite gets the palette's border colour on its outline and its background tint behind the title strip; nested composites each take the next colour, so depth stays readable. The body keeps the theme's owncompositeBackground.redux-dark-colorcolours the outlines only, matching how it treats ER, requirement and sequence.The concurrency regions produced by a
--divider share the colour of the composite they split, rather than taking one of their own — the author wrote a single composite, so it is drawn as one thing in parts. Adding a--therefore leaves every other composite's colour untouched.Under the
handDrawnlook, concurrency regions are now filled solid rather than hatched, so that they can carry the palette tint the same way every other look does.States inside a composite stay uniform. A composite carrying its own
classDeforstylekeeps those colours and takes no palette slot, and neither do its concurrency regions; the slot is still spent, so styling one composite does not shift the colours of the ones after it. Note that this opt-out is all-or-nothing: aclassDefthat sets only text properties, such asfont-weight, still takes that composite out of the palette. -
#8176
16b9a7dThanks @ashishjain0512! - feat(themes): swimlane lanes take a per-lane colour under theredux-colorandredux-dark-colorthemes, cycling every 12 as flowchart subgraphs do. The lane holding ungrouped nodes takes its own slot and now follows the diagram'slook. -
#8178
75e6c30Thanks @knsv-bot! - feat(usecase): use case diagrams now take colour by role — actors, use cases and system boundaries each get their own colour from the newusecaseActorBkg/usecaseActorBorder,usecaseBkg/usecaseBorderandusecaseBoundaryBkg/usecaseBoundaryBordertheme variables, withusecaseIncludeLine/usecaseExtendLineseparating the two dashed relationship kinds by hue.redux-colorandredux-dark-colorset them; every other theme is unchanged. Colour is keyed to the kind of element, so editing a diagram never recolours the elements around the edit. Setusecase.colorScheme: 'rotate'for the per-element palette cycle instead, andclassDef/stylestill wins over both.
-
#8211
dc2e453Thanks @ashishjain0512! - fix(block): restore node borders under looks that use a gradient stroke.A block diagram rendered with
look: neolost its node borders entirely. The look-specific rules setstroke: url(#<svgId>-gradient), and those definitions were appended byrendering-util/render.ts— which block does not use, since it runs its own render loop. Anurl(#…)paint that resolves to nothing is painted as none, not as a fallback colour, so the border disappeared rather than degrading to a plain stroke.The definitions now live in
rendering-util/insertLookDefs.tsand both render paths call it, so the two cannot drift into producing different gradients.This only became visible recently: block did not previously pass
lookto its nodes, so none of the look-specific rules applied to it and the missing definitions went unnoticed. -
#8181
ce0302dThanks @knsv-bot! - fix(block): apply the redux colour palette to composite blocks. Composites now take a per-container colour underredux-colorandredux-dark-color, matching how flowchart subgraphs are coloured — one counter over containers, in declaration order, with the plain shapes left on the flat theme colour. The palette is opt-in: block does not default toredux-colorthe way flowchart and several other diagram types do, so settheme: redux-color(orredux-dark-color) explicitly to see it.classDef/stylestill win. -
#8211
ad98070Thanks @ashishjain0512! - fix(block): take the node border width from the theme, as every other diagram does.block/styles.tspinnedstroke-width: 1pxon node shapes while the flowchart, class and state stylesheets all readstrokeWidthfrom the theme. Under a theme that asks for more —neoasks for 2 — a block rendered a visibly thinner border than an identical flowchart node, and nothing downstream corrected it: theneorules in the shared stylesheet setstrokeandfilter, neverstroke-width.The gap was invisible until block began propagating
lookto its nodes, because until then none of the look-specific rules applied to block at all.Affects only themes whose
strokeWidthis not 1.neois 2;neo-dark,baseand the rest are unchanged. -
#8231
e2aab3fThanks @knsv-bot! - Use balanced Brandes-Koepf placement for the default ELK preset to improve alignment of branches and composite-state entries. Preserve named non-default presets and explicit placement/alignment options, includingNONE. Some diagrams may become wider or taller;depthFirstretains the previous default layout. -
#7874
6b79518Thanks @filipsajdak! - fix(c4): allow boundaries as relationship endpointsA
Rel(orBiRel/Rel_*) that referenced a boundary alias threwreferences an unknown shape, because endpoint lookup only searched shapes and not boundaries. Boundary aliases now resolve as relationship endpoints. Fixes #4864. -
#8100
30325d4Thanks @filipsajdak! - fix(c4): stop wrapping non-text named attributes that land in a text slotA named attribute such as
$tagsor$spritecan arrive in the positional slot of a text field when the argument before it is omitted. It was then stored as{ text: value }rather than a string, so$tagsgiven without a description crashed rendering. -
#8148
c3ee3c7Thanks @ashishjain0512! - fix(themes):redux-dark,redux-dark-colorandneo-darkshippedsecondBkgas the literal stringcalculated, so railroad rendered the invalidfill: calculated; it is now computed astheme-darkdoes. The same three themes had gantt done-task labels at 1.07:1 contrast — a light fill under their light task ink — now 5.7:1 or better. The ER and requirement stylesheets also validatelookbefore interpolating it into a CSS selector. -
#8222
1ee934dThanks @ashishjain0512! - fix: stop ELK-laid-out diagrams loggingUnknown arrow type: arrow_openonce per edge.ELK's arrow table put the edge type
arrow_open— which means "no arrowheads" — into the arrow-type slot, wherenoneis the value that marks a deliberate absence. Anything else is reported as an unknown marker name. Diagrams whose db setsarrowTypeStartitself, such as flowcharts, never reached that fallback; state diagrams, which leave it unset, warned on every edge.Rendering is unchanged: both spellings produced no start marker and the same geometry, since neither has a marker offset. Only the log noise goes away.
-
#8204
a7831c5Thanks @knsv-bot! - fix: keep ELK subgraph frames out to the border anchor of edges that terminate on the group, so transitions to composite states no longer run along the cluster border -
#8155
0320406Thanks @ashishjain0512! - fix(elk): a composite state or subgraph containing a loop now opens on its own start node.Cycle breaking was resolved for the root graph but never passed to containers, so a container laid out on its own fell back to ELK's default,
GREEDY, while the root ran whateverelk.presetasked for. The two then reversed different edges of the same cycle.In a composite state that loops, greedy reverses the edge that turns an ordinary state into a source, and sources sit on the first layer — so the diagram opened on that state alongside its start circle instead of on the start circle alone. Dagre reverses the other edge, which is why the two layout engines disagreed on the same diagram.
Containers now resolve cycle breaking from the preset exactly as the root does. Only containers that actually contain a cycle change; an acyclic subgraph gives a cycle-breaking strategy nothing to do.
preset: legacystill reaches containers withGREEDY, so it keeps reproducing the earlier rendering inside frames as well as outside them. -
#8228
e36b883Thanks @knsv-bot! - Reserve subgraph title padding before ELK routes edges so attachments stay on the painted frame. Clip stale interior endpoints along the incoming segment, avoiding edges that run along the subgraph border. -
#8199
b993915Thanks @knsv-bot! - fix: keep ELK edges vertical at small nodes — the node now moves onto the routed line instead of the edge bending toward the node's off-centre position -
#8155
4e00c5cThanks @ashishjain0512! - fix: render ER relationship edges above subgraph backgrounds under the ELK layout instead of hiding them behind the cluster rect -
#8156
3f05015Thanks @ashishjain0512! - fix(er, requirement, timeline): stop the ER, requirement and timeline stylesheets emitting CSS the browser discards for the colour themes.All three generate one rule per palette slot. ER and requirement looped to
THEME_COLOR_LIMITand indexed the palette by the loop counter, which goes wrong in both directions: a palette shorter than the limit emittedstroke: undefinedfor the overflow slots, and a palette longer than it left entities stamped with a slot that had no rule at all, rendering unstyled beside coloured neighbours. Both now take the loop bound from the palette itself, which is the same length the boxes stamp with — so the rules emitted and the slots stamped cannot disagree. Both also bail before the loop for an empty palette, since wrapping alone is not enough there:i % 0isNaNand[][NaN]isundefined.Timeline keeps looping to
THEME_COLOR_LIMITand wrapping, because it numbers.section-Nclasses rather than palette slots — nothing stamps those, so the palette cycles across however many sections exist.requirementalso emittedfill: ;— a property with no value, which is invalid — whenever there was no background palette. That is the live case forredux-dark-color, which ships a border palette and no background palette so that it colours outlines only. The declaration is now omitted instead.None of these raises an error: the browser discards the invalid declaration, so the only symptom is a shape rendering unstyled.
-
#8149
d57ed55Thanks @ashishjain0512! - fix(docs): stopdocs:builddeleting the committeddocs/directory when a later step fails. -
#7906
1fc5bb3Thanks @knsv-bot! - perf: avoid quadratic parse cost on deeply-indented diagrams (~1.4 s → ~30 ms on a pathological fixture) -
#8211
b979eb4Thanks @ashishjain0512! - fix(handdrawn): tighten the hachure fill so shapes read as filled rather than striped.The hand-drawn fill drew 4px-wide strokes 5.2px apart, which left visible diagonal banding across every shape — legible as a texture up close, but at normal diagram size it read as stripes rather than a fill. Strokes are now 1.5px at 1.5px spacing, so the fill reads as an even tint while keeping the drawn edge quality.
The striped fill behind state-diagram start, end and fork markers moves the same way, so those come out close to solid — which is what the UML convention asks for anyway.
Swimlane lane titles previously pinned their own stroke weight and so ignored the shared value; they now follow it like every other shape.
-
#8152
7ee4c3fThanks @knsv-bot! - fix: edges attach to non-rectangular shapes on the outline instead of half a pixel off it.This is not ELK-specific.
intersectPolygonis how every non-rectangular shape finds its edge attachment — diamond, stadium, hexagon, trapezoid, subroutine — in every layout, so a dagre-rendered flowchart with a decision diamond is affected exactly as much as an ELK one.intersectLinecomes from Graphics Gems, where the coordinates were integers and the numerator was nudged half a denominator away from zero so the integer division rounded instead of truncating. JavaScript division does neither, so the nudge stopped being a correction and became the whole error: every result came back displaced by0.5 * sign(num) * sign(denom). The magnitude is always exactly half a unit, but the sign is per axis, because the numerator is computed separately for x and y while the denominator is shared — so the two axes could move the same way or opposite ways depending on the geometry, which is why it never looked like a constant offset anyone could spot by eye.That was enough to give an otherwise orthogonal edge a tiny diagonal opening segment, and to put an attachment just inside the node it was meant to touch.
question.tshad been subtracting a flat 0.5 from both axes to compensate for diamonds. That only cancelled the bias when both signs came out positive, and doubled the error to a full unit when they did not — so the compensation goes along with the cause. Rendered output moves by up to a pixel wherever a polygon shape terminates an edge. -
#8048
a6ec7ffThanks @knsv-bot! - fix: treat</br>as a line break in labelslineBreakRegexonly matched<br>,<br/>and<br />, so the malformed-but-common</br>survived as literal text wherever labels render as plain SVG text. HTML parsers already treat</br>as a<br>, so the tag appeared to work in HTML-label mode and failed everywhere else. It is now accepted on both paths.Also fixes
hasBreaks()returning alternating results for the same input: it called.test()on a global regex, which advanceslastIndexbetween calls.wrapLabel()used the same stateful check and could therefore re-wrap labels that already contained breaks. -
#8152
1246a55Thanks @knsv-bot! - fix: don't draw a line hop that has no room next to a bend.A crossing close to a corner used to get a hop squeezed into whatever space was left — as little as 2.9px against a requested 6px, opening exactly on the corner's tangent point. At that size the arc no longer clears the line it is meant to hop, so the two strokes still touch and the corner's curve runs straight into the arc's. It reads as a rendering fault rather than as a crossing.
Hops now keep a straight run clear of the bend, and one that would still have to shrink below 60% of the requested radius is dropped instead of drawn. An undrawn hop is an ordinary crossing, which is a much better failure than a broken-looking one.
This changes swimlane diagrams as well as ELK ones. Swimlanes are the existing consumer of line hops and have them on today, so a swimlane with a crossing near a bend will render differently even for someone not using ELK at all. It shows up wherever a layout stacks edges in narrow lanes: ELK routes subgraph-internal edges 10px apart, and 10px does not hold a 7.07px corner cut plus a 6px hop.
A crossing is also ignored now when it lands inside the stretch where either edge is rounding a bend. Crossings are found on polylines, but a rounded edge is not drawn as its polyline — it leaves the line up to 7.07px before each bend and rejoins it that far after. A crossing found inside that stretch is somewhere the stroke never goes, so the hop was arching over blank paper while the two lines carried on touching beside it.
-
#8146
def4c81Thanks @ashishjain0512! - fix(themes):redux-colorandredux-dark-colornow define every variable their base themes define, instead of silently falling back to untuned values forprimaryBorderColor,clusterBkg,clusterBorder,altBackground,compositeTitleBackgroundand the state and requirement edge-label backgrounds. Pie, gantt and user-journey also read the theme palette rather than a single pale tint, so pie slices are distinguishable and gantt sections are visibly banded. -
#8189
3802472Thanks @ashishjain0512! - fix(themes): venn circles follow theredux-colorandredux-dark-colorpalettes. Neither theme defined anyvenn*variable, so every circle rendered in one flat colour. -
#8185
f5af413Thanks @knsv-bot! - fix: sequence diagram actors under theneolook andreduxthemes. Notes no longer render bold, arectsection band is no longer drawn white on white, and participants follow one vertical model: every lifeline starts and ends on a shared line, all participant glyphs are one size with their feet on a common edge, single-line labels share one baseline in the header and in the mirrored footer, and a multiline label grows away from the lifeline instead of moving it. Theclassiclook is unchanged. -
#8157
a3a92baThanks @ashishjain0512! - fix(sequence): index each actor colour palette by its own length.Every actor-drawing call site read
bkgColorArray[actorCount % borderColorArray.length]— one palette indexed by the other's length. Underredux-colorboth palettes have twelve entries, so the wrong length happens to give the right answer and nothing is visibly broken today. It goes wrong as soon as the two differ in length: the overflow actors resolve toundefined, and d3 then strips the inline fill for some actors and not others.Both palettes now cycle within their own length, via a shared helper. An absent or empty palette still yields
undefinedrather than a substitute colour, which is whatredux-dark-colorrelies on: it ships a border palette and an empty background palette so actors are outlined but not filled. -
#8232
e691042Thanks @ashishjain0512! - Circle, double circle, Delay and Display nodes size themselves from the label's height as well as its width, so a wrapped label stays inside the outline. Each was sized from the width alone and clipped any label taller than it was wide, whichflowchart.wrappingWidth: 120made common.This replaces the wrapping exemption those shapes carried along with stadium and diamond: it stopped them wrapping at all, so a long label produced a single-line node hundreds of pixels wide. Stadium needs no exemption — its
w >= 1.5hfloor already keeps a wrapped label from closing the caps into a circle — and diamond never needed one.Stacked (
docs) and lined (lin-doc) document nodes lift their label clear of the wavy bottom edge by the wave's full depth rather than half of it, so the last line is no longer clipped by the trough. Plaindocalready did this; the two variants had copied it with the offset halved. Node dimensions are unchanged. -
#8154
ba0deedThanks @aloisklink! - fix: bundlefastdomto fix issues with globaldefine -
#8227
b05824aThanks @knsv-bot! - fix(flowchart): stop stadium labels from wrapping into a circle and exempt selected shapes from automatic label wrapping.Long stadium labels wrapped to
flowchart.wrappingWidth, grew taller without growing wider, and turned the stadium into a circle. Stadium now keeps its semicircular caps with an outer width of at least 1.5 times its height and enough clearance that the label stays inside the arcs.Stadium (
terminal,pill), circle (circ), diamond (diam,decision,question), double circle (dbl-circ,double-circle), Display (curv-trap,curved-trapezoid,display) and Delay (delay,half-rounded-rectangle) no longer wrap their labels atflowchart.wrappingWidth. Explicit line breaks are kept, and an explicit node width still takes precedence. -
#8192
0ec29e9Thanks @ashishjain0512! - fix(state): draw the edge between a state and its note dashed under theneolook. It was rendering solid.The dashes were expressed only as CSS —
.note-edge { stroke-dasharray: 5 }— which is enough underclassicbut not underneo, whereinsertEdgewrites an inlinestroke-dasharrayon every edge, computed from the path length so the arrow markers keep their gaps. An inline style outranks a stylesheet rule, so the note edge took the solid pattern and the.note-edgerule simply lost. The note edge now declarespattern: 'dashed', which is whatinsertEdgereads to choose its dash generator.classicis unchanged. -
#8225
0d77926Thanks @knsv-bot! - fix(swimlane): seed cycle removal from source nodes so feedback edges are reversed instead of forward flow edges. A cycle liketask --> decision --> fix --> taskno longer placesfixin the middle of thestart --> taskflow line. -
#8211
93edd72Thanks @ashishjain0512! - fix: keep association markers apart on use-case ovals under ELK. Ovals get the same vertical padding as horizontal, edge attachment points on them are spread across the side, and the ELK renderer now accounts for every end-marker offset (extension, circle, cross, …) so a short entry stub can no longer flip a marker around. -
Updated dependencies [
33442fd]:- @mermaid-js/parser@2.0.0
Mermaid 12 changes what unconfigured diagrams look like: ELK replaces dagre as the default layout, and redux-color / neo replace default / classic as theme and look for flowchart, class, state, ER, requirement, sequence, use case, swimlane, Venn and agentflow. If you embed Mermaid and want your users' existing diagrams to render as before, set all three explicitly at initialisation:
mermaid.initialize({ layout: 'dagre', theme: 'default', look: 'classic' });
Diagram authors can do the same per diagram in front matter, and a value there always wins over initialize().
@mermaid-js/tiny@12.0.0
-
#8213
33442fdThanks @aloisklink! - chore!: require ES2024, Safari 17.4+, Node.JS v22.12+Mermaid is now built to target Safari 17.4+ and ES2024. If you need to support older browsers, you may need to polyfill or transpile mermaid.
Safari 17.4+ has been chosen as the floor, as unlike Firefox/Chrome, older iOS devices don't get major Safari updates.
Node.JS v22.12+ is also declared as requirement in our
package.jsonfiles, but as mermaid requires a browser, this is mainly so we can use dependencies that also declare a Node.JS v22.12+ requirement, without causing issues for users when runningnpm install. -
#8155
810893cThanks @ashishjain0512! - feat!: ELK is now bundled with mermaid and is the default layout algorithm.ELK previously shipped as a separate
@mermaid-js/layout-elkpackage that sites had to install and register. It is now part of mermaid itself and registered automatically, solayout: elk— and theelk.stress,elk.force,elk.mrtree,elk.sporeOverlap,elk.boxandelk.rectpackingvariants — work with no setup.This changes how existing diagrams look. Flowchart, state, class, entity-relationship, requirement, use-case and agentflow diagrams that do not specify a
layoutare now laid out by ELK instead of dagre. To keep the previous layout, set dagre explicitly:--- config: layout: dagre ---
or globally,
mermaid.initialize({ layout: 'dagre' }).Mindmap is unchanged: it keeps laying out with cose-bilkent unless a layout is explicitly requested (in the tiny build, which ships neither ELK nor cose-bilkent, it falls back to dagre).
Other notes:
- ELK is loaded as a separate chunk in the ESM builds, so it is only fetched when a diagram actually uses it. The single-file IIFE build (
mermaid.min.js) inlines it and grows by roughly 500 kB gzipped. - The tiny build deliberately omits ELK to stay small, and falls back to dagre for diagrams that ask for an ELK layout. Its size is unchanged.
@mermaid-js/layout-elkis no longer needed on normal builds — existingmermaid.registerLayoutLoaders(elkLayouts)calls keep working and can be removed. It is still published, and remains the way to add ELK to the tiny build.dist/mermaid.esm.min.mjsnow contains syntax thates-module-lexer(used by Vite) rejects withcontent contains invalid JS syntax. Bundler users are unaffected as long as they import themermaidpackage specifier, which resolves to the core build; only builds that point Vite directly at that dist file need to switch to the package specifier or the core build.- State diagrams now resolve their layout through the same registration check as every other diagram, so an unavailable layout falls back instead of failing to render.
- ELK is loaded as a separate chunk in the ESM builds, so it is only fetched when a diagram actually uses it. The single-file IIFE build (
-
#8148
8603bddThanks @ashishjain0512! -redux-coloris now the default theme andneothe default look for ten diagram types — flowchart, swimlane, class, ER, requirement, sequence, state, use case, Venn and agentflow. Those rendered without an explicitthemeandlookchange appearance; every other diagram type keepsdefaultandclassic. To keep the previous appearance, set both explicitly —mermaid.initialize({ theme: 'default', look: 'classic' }), or the same two keys underconfigin front matter.An unrecognised
themename now resolves to the default theme in name as well as in variables; previously the invalid name stayed in place while the default's variables were loaded, and every palette-aware stylesheet gates on the name.theme: 'null', the documented way to disable the pre-defined themes, is unaffected. Note thatneopaints node strokes with a gradient when the theme setsuseGradient, whichbasedoes; setting a customnodeBorderonbaseturns the gradient off. -
#8211
a19bd08Thanks @ashishjain0512! - Removed: thedefaultRendereroption of theflowchart,classandstateconfig sections.Use the top-level
layoutoption instead:--- config: layout: dagre # or elk ---
defaultRenderernamed a rendering engine back when there was more than one. There is not. All of its accepted values —dagre,dagre-wrapper,dagre-d3— had come to select the same unified renderer, and itselkvalue did nothing but setlayout: elkon your behalf. The option's documented purpose no longer existed.What it still did was stranger than doing nothing. The detectors branched on whether the value was recognised, so a valid value routed a diagram to the modern renderer and an invalid one silently routed it to the legacy renderer. Since the default was always a valid value, the legacy
classandstaterenderers were unreachable except by misconfiguring the option.layoutselects the layout algorithm, as it already did. Nothing else does.graph,classDiagramandstateDiagramalways render with the unified diagram. This was already true for every valid configuration.- The legacy
flowchart,classandstatediagram ids are gone.flowchart-v2,classDiagramandstateDiagramare whatdetectTypenow returns for that syntax. flowchart-elkas an explicit diagram keyword still works.
Configuration that still sets
defaultRendereris ignored rather than rejected, so nothing throws — but it no longer has any effect, and diagrams that relied onflowchart: { defaultRenderer: 'elk' }to get an ELK layout should setlayout: elk. -
#8223
6df1149Thanks @aloisklink! - refactor!: remove layout internal exportsRemove the
clearLayoutRenderState,createCommonLayoutRenderer,defaultMeasureLayout,paintLayoutDatafunctions, and theCommonLayout*types from Mermaid's public API.These functions were only used by the
@mermaid-js/layout-elkpackage, which now bundles them. Removing them makes it easier to avoid major versions of mermaid.
-
#8048
7a3c1a8Thanks @knsv-bot! - feat: add UML use case diagrams with actors, use cases, system boundaries, relationships, stereotypes, notes, JSON tables, class and style support, accessibility metadata, and business variants. -
#8073
0cf3797Thanks @knsv-bot! - feat: add theagentflow-betadiagram type for describing agentic workflows — agents, the flows they run, the tasks and tools inside them, and how control and data move between them.agentflow-beta TB flow reviewer["Review Agent"] changes["Gather changes"]@{ shape: input } analyse["Analyse"]@{ shape: task } lint["run_linter"]@{ shape: tool } changes --> analyse --> lint endflow … endcontainers, aglobal … endscope block for nodes that must stay outside their referencing flow, andconnectordeclarations for external systems.- Node shapes addressed by domain-facing aliases (
task,tool,input,decision,refdoc,action) via inline@{ … }metadata, and@{ view: collapsed }to fold a container down to a summary node. - Edge operators carry semantics:
-->sequence,-.-reference,--xfailure, plus the labelled-branch form. getSemanticModel()projects the parsed diagram into a presentation-free semantic view for downstream consumers, andgetDiagnostics()reports structured warnings with source positions.- A
flowContainerStroketheme variable across all built-in themes, and anagentflowconfig namespace for spacing, padding, and title margin.
The diagram is beta: the syntax may still change before it is declared stable.
-
#8197
846fd65Thanks @knsv-bot! - feat(agentflow): take the redux colour palette. Underredux-colorandredux-dark-color, every agentflow node kind — tool, task, decision, input, refdoc, connector, action — gets its own colour from a fixed palette slot, so colour says what an element is and stays put when the diagram is edited around it. Containers cycle a counter in declaration order, the way flowchart subgraphs do, from the slots above the kind range so a container frame never matches a node inside it. A collapsed container keeps its slot.redux-dark-colorcarries borders but no fills, so nodes there take palette strokes over the theme's own background. Agentflow also takesredux-colorandneoas its own per-diagram defaults, so an agentflow diagram that sets neither renders with the palette; anything set in front matter, a directive orinitialize()still wins. -
#8073
cd48a64Thanks @knsv-bot! - feat:defaultMeasureLayoutaccepts aunwrapGroupLabelsoption so a layout engine can ask for cluster labels to be measured at their natural width instead of theflowchart.wrappingWidthfallback.insertClusterpaints a plain cluster label with an infinite width while the measurement pass wrapped it at 200px, so a layout that sizes compound nodes from the measured label sized them too narrow. Layouts now opt in explicitly; core no longer inspects the layout's name. Markdown cluster labels stay wrapped, since those are painted wrapped.The bundled ELK layout opts in, which changes the size of labelled subgraphs in ELK-laid-out diagrams. Diagrams laid out with dagre are unaffected.
-
#8073
0cf3797Thanks @knsv-bot! - feat: let a container pick its own ELK algorithm with@{ algorithm: … }, and addelk.boxandelk.rectpackingto the selectablelayoutvalues.A subgraph carrying
@{ algorithm: elk.box }is laid out with that algorithm in its own coordinate system instead of inheriting the diagram's. Supported values areelk.layered,elk.box,elk.rectpacking,elk.stress,elk.force,elk.mrtree,elk.radial, andelk.sporeOverlap; anything else is ignored with a warning rather than handed to ELK, where an unknown id would abort the layout. Containers with edges crossing their boundary fall back to the inherited algorithm, since isolated layout and cross-boundary edges are incompatible.Also in this release:
- ELK cluster labels are measured unwrapped, so a compound node is sized to fit its label instead of to a 200px wrapping width. This changes the size — and therefore the layout — of existing ELK diagrams that have labelled subgraphs.
elk.boxandelk.rectpackingplace nodes but never route edges. Edges they leave unrouted now fall back to a straight line between the two node centres instead of failing the render.keepEntryNodeOnToppins a cycle's true entry rather than the first-declared node when a back-edge feeds the entry, so recursive flows read from where they actually start.
-
#8193
813c766Thanks @ashishjain0512! -theme,lookandlayoutcan now be set per diagram type, in each diagram's config section —mermaid.initialize({ look: 'classic', flowchart: { look: 'handDrawn' } }), or the same underconfigin front matter. The schema uses the same mechanism to give a diagram type its own default. Resolution, highest first: front matter or directive,initialize(), the diagram type's default, the global default; a diagram-scoped value beats a global one set in the same layer.Swimlanes take
layout: swimlanefrom that schema default instead of having it forced by theirinithook, soswimlane: { layout: ... }and alayoutin front matter are now honoured. An unregistered layout also always falls back todagrewith a warning rather than throwing — state diagrams skipped that fallback, and mindmaps threw whencose-bilkentwas absent, as it is in@mermaid-js/tiny. -
#8147
43d9fbcThanks @ashishjain0512! - feat(themes): class boxes and flowchart subgraph containers now take a per-item colour under theredux-colorandredux-dark-colorthemes, cycling every 12 as ER entities already do. Collapsed subgraphs keep the slot they would have had expanded; nodes inside a subgraph stay uniform, and explicitclassDeforstylestill wins over the palette. -
#8191
2878cf3Thanks @ashishjain0512! - feat(themes): composite states now take a per-container colour under theredux-colorandredux-dark-colorthemes, as flowchart subgraphs already do. Each composite gets the palette's border colour on its outline and its background tint behind the title strip; nested composites each take the next colour, so depth stays readable. The body keeps the theme's owncompositeBackground.redux-dark-colorcolours the outlines only, matching how it treats ER, requirement and sequence.The concurrency regions produced by a
--divider share the colour of the composite they split, rather than taking one of their own — the author wrote a single composite, so it is drawn as one thing in parts. Adding a--therefore leaves every other composite's colour untouched.Under the
handDrawnlook, concurrency regions are now filled solid rather than hatched, so that they can carry the palette tint the same way every other look does.States inside a composite stay uniform. A composite carrying its own
classDeforstylekeeps those colours and takes no palette slot, and neither do its concurrency regions; the slot is still spent, so styling one composite does not shift the colours of the ones after it. Note that this opt-out is all-or-nothing: aclassDefthat sets only text properties, such asfont-weight, still takes that composite out of the palette. -
#8176
16b9a7dThanks @ashishjain0512! - feat(themes): swimlane lanes take a per-lane colour under theredux-colorandredux-dark-colorthemes, cycling every 12 as flowchart subgraphs do. The lane holding ungrouped nodes takes its own slot and now follows the diagram'slook. -
#8178
75e6c30Thanks @knsv-bot! - feat(usecase): use case diagrams now take colour by role — actors, use cases and system boundaries each get their own colour from the newusecaseActorBkg/usecaseActorBorder,usecaseBkg/usecaseBorderandusecaseBoundaryBkg/usecaseBoundaryBordertheme variables, withusecaseIncludeLine/usecaseExtendLineseparating the two dashed relationship kinds by hue.redux-colorandredux-dark-colorset them; every other theme is unchanged. Colour is keyed to the kind of element, so editing a diagram never recolours the elements around the edit. Setusecase.colorScheme: 'rotate'for the per-element palette cycle instead, andclassDef/stylestill wins over both. -
#8211
a31ecb7Thanks @ashishjain0512! - feat: addwrappingWidthandminNodeWidthconfig to flowchart, state, usecase, and agentflow diagrams.wrappingWidth(now also available for state, usecase, and agentflow diagrams) caps the label width at 120 by default; the newminNodeWidthwidens short labels to 120 by default so nodes get a uniform width.
-
#8211
dc2e453Thanks @ashishjain0512! - fix(block): restore node borders under looks that use a gradient stroke.A block diagram rendered with
look: neolost its node borders entirely. The look-specific rules setstroke: url(#<svgId>-gradient), and those definitions were appended byrendering-util/render.ts— which block does not use, since it runs its own render loop. Anurl(#…)paint that resolves to nothing is painted as none, not as a fallback colour, so the border disappeared rather than degrading to a plain stroke.The definitions now live in
rendering-util/insertLookDefs.tsand both render paths call it, so the two cannot drift into producing different gradients.This only became visible recently: block did not previously pass
lookto its nodes, so none of the look-specific rules applied to it and the missing definitions went unnoticed. -
#8181
ce0302dThanks @knsv-bot! - fix(block): apply the redux colour palette to composite blocks. Composites now take a per-container colour underredux-colorandredux-dark-color, matching how flowchart subgraphs are coloured — one counter over containers, in declaration order, with the plain shapes left on the flat theme colour. The palette is opt-in: block does not default toredux-colorthe way flowchart and several other diagram types do, so settheme: redux-color(orredux-dark-color) explicitly to see it.classDef/stylestill win. -
#8211
ad98070Thanks @ashishjain0512! - fix(block): take the node border width from the theme, as every other diagram does.block/styles.tspinnedstroke-width: 1pxon node shapes while the flowchart, class and state stylesheets all readstrokeWidthfrom the theme. Under a theme that asks for more —neoasks for 2 — a block rendered a visibly thinner border than an identical flowchart node, and nothing downstream corrected it: theneorules in the shared stylesheet setstrokeandfilter, neverstroke-width.The gap was invisible until block began propagating
lookto its nodes, because until then none of the look-specific rules applied to block at all.Affects only themes whose
strokeWidthis not 1.neois 2;neo-dark,baseand the rest are unchanged. -
#8231
e2aab3fThanks @knsv-bot! - Use balanced Brandes-Koepf placement for the default ELK preset to improve alignment of branches and composite-state entries. Preserve named non-default presets and explicit placement/alignment options, includingNONE. Some diagrams may become wider or taller;depthFirstretains the previous default layout. -
#7874
6b79518Thanks @filipsajdak! - fix(c4): allow boundaries as relationship endpointsA
Rel(orBiRel/Rel_*) that referenced a boundary alias threwreferences an unknown shape, because endpoint lookup only searched shapes and not boundaries. Boundary aliases now resolve as relationship endpoints. Fixes #4864. -
#8100
30325d4Thanks @filipsajdak! - fix(c4): stop wrapping non-text named attributes that land in a text slotA named attribute such as
$tagsor$spritecan arrive in the positional slot of a text field when the argument before it is omitted. It was then stored as{ text: value }rather than a string, so$tagsgiven without a description crashed rendering. -
#8148
c3ee3c7Thanks @ashishjain0512! - fix(themes):redux-dark,redux-dark-colorandneo-darkshippedsecondBkgas the literal stringcalculated, so railroad rendered the invalidfill: calculated; it is now computed astheme-darkdoes. The same three themes had gantt done-task labels at 1.07:1 contrast — a light fill under their light task ink — now 5.7:1 or better. The ER and requirement stylesheets also validatelookbefore interpolating it into a CSS selector. -
#8222
1ee934dThanks @ashishjain0512! - fix: stop ELK-laid-out diagrams loggingUnknown arrow type: arrow_openonce per edge.ELK's arrow table put the edge type
arrow_open— which means "no arrowheads" — into the arrow-type slot, wherenoneis the value that marks a deliberate absence. Anything else is reported as an unknown marker name. Diagrams whose db setsarrowTypeStartitself, such as flowcharts, never reached that fallback; state diagrams, which leave it unset, warned on every edge.Rendering is unchanged: both spellings produced no start marker and the same geometry, since neither has a marker offset. Only the log noise goes away.
-
#8204
a7831c5Thanks @knsv-bot! - fix: keep ELK subgraph frames out to the border anchor of edges that terminate on the group, so transitions to composite states no longer run along the cluster border -
#8155
0320406Thanks @ashishjain0512! - fix(elk): a composite state or subgraph containing a loop now opens on its own start node.Cycle breaking was resolved for the root graph but never passed to containers, so a container laid out on its own fell back to ELK's default,
GREEDY, while the root ran whateverelk.presetasked for. The two then reversed different edges of the same cycle.In a composite state that loops, greedy reverses the edge that turns an ordinary state into a source, and sources sit on the first layer — so the diagram opened on that state alongside its start circle instead of on the start circle alone. Dagre reverses the other edge, which is why the two layout engines disagreed on the same diagram.
Containers now resolve cycle breaking from the preset exactly as the root does. Only containers that actually contain a cycle change; an acyclic subgraph gives a cycle-breaking strategy nothing to do.
preset: legacystill reaches containers withGREEDY, so it keeps reproducing the earlier rendering inside frames as well as outside them. -
#8228
e36b883Thanks @knsv-bot! - Reserve subgraph title padding before ELK routes edges so attachments stay on the painted frame. Clip stale interior endpoints along the incoming segment, avoiding edges that run along the subgraph border. -
#8199
b993915Thanks @knsv-bot! - fix: keep ELK edges vertical at small nodes — the node now moves onto the routed line instead of the edge bending toward the node's off-centre position -
#8155
4e00c5cThanks @ashishjain0512! - fix: render ER relationship edges above subgraph backgrounds under the ELK layout instead of hiding them behind the cluster rect -
#8156
3f05015Thanks @ashishjain0512! - fix(er, requirement, timeline): stop the ER, requirement and timeline stylesheets emitting CSS the browser discards for the colour themes.All three generate one rule per palette slot. ER and requirement looped to
THEME_COLOR_LIMITand indexed the palette by the loop counter, which goes wrong in both directions: a palette shorter than the limit emittedstroke: undefinedfor the overflow slots, and a palette longer than it left entities stamped with a slot that had no rule at all, rendering unstyled beside coloured neighbours. Both now take the loop bound from the palette itself, which is the same length the boxes stamp with — so the rules emitted and the slots stamped cannot disagree. Both also bail before the loop for an empty palette, since wrapping alone is not enough there:i % 0isNaNand[][NaN]isundefined.Timeline keeps looping to
THEME_COLOR_LIMITand wrapping, because it numbers.section-Nclasses rather than palette slots — nothing stamps those, so the palette cycles across however many sections exist.requirementalso emittedfill: ;— a property with no value, which is invalid — whenever there was no background palette. That is the live case forredux-dark-color, which ships a border palette and no background palette so that it colours outlines only. The declaration is now omitted instead.None of these raises an error: the browser discards the invalid declaration, so the only symptom is a shape rendering unstyled.
-
#8149
d57ed55Thanks @ashishjain0512! - fix(docs): stopdocs:builddeleting the committeddocs/directory when a later step fails. -
#7906
1fc5bb3Thanks @knsv-bot! - perf: avoid quadratic parse cost on deeply-indented diagrams (~1.4 s → ~30 ms on a pathological fixture) -
#8211
b979eb4Thanks @ashishjain0512! - fix(handdrawn): tighten the hachure fill so shapes read as filled rather than striped.The hand-drawn fill drew 4px-wide strokes 5.2px apart, which left visible diagonal banding across every shape — legible as a texture up close, but at normal diagram size it read as stripes rather than a fill. Strokes are now 1.5px at 1.5px spacing, so the fill reads as an even tint while keeping the drawn edge quality.
The striped fill behind state-diagram start, end and fork markers moves the same way, so those come out close to solid — which is what the UML convention asks for anyway.
Swimlane lane titles previously pinned their own stroke weight and so ignored the shared value; they now follow it like every other shape.
-
#8152
7ee4c3fThanks @knsv-bot! - fix: edges attach to non-rectangular shapes on the outline instead of half a pixel off it.This is not ELK-specific.
intersectPolygonis how every non-rectangular shape finds its edge attachment — diamond, stadium, hexagon, trapezoid, subroutine — in every layout, so a dagre-rendered flowchart with a decision diamond is affected exactly as much as an ELK one.intersectLinecomes from Graphics Gems, where the coordinates were integers and the numerator was nudged half a denominator away from zero so the integer division rounded instead of truncating. JavaScript division does neither, so the nudge stopped being a correction and became the whole error: every result came back displaced by0.5 * sign(num) * sign(denom). The magnitude is always exactly half a unit, but the sign is per axis, because the numerator is computed separately for x and y while the denominator is shared — so the two axes could move the same way or opposite ways depending on the geometry, which is why it never looked like a constant offset anyone could spot by eye.That was enough to give an otherwise orthogonal edge a tiny diagonal opening segment, and to put an attachment just inside the node it was meant to touch.
question.tshad been subtracting a flat 0.5 from both axes to compensate for diamonds. That only cancelled the bias when both signs came out positive, and doubled the error to a full unit when they did not — so the compensation goes along with the cause. Rendered output moves by up to a pixel wherever a polygon shape terminates an edge. -
#8048
a6ec7ffThanks @knsv-bot! - fix: treat</br>as a line break in labelslineBreakRegexonly matched<br>,<br/>and<br />, so the malformed-but-common</br>survived as literal text wherever labels render as plain SVG text. HTML parsers already treat</br>as a<br>, so the tag appeared to work in HTML-label mode and failed everywhere else. It is now accepted on both paths.Also fixes
hasBreaks()returning alternating results for the same input: it called.test()on a global regex, which advanceslastIndexbetween calls.wrapLabel()used the same stateful check and could therefore re-wrap labels that already contained breaks. -
#8152
1246a55Thanks @knsv-bot! - fix: don't draw a line hop that has no room next to a bend.A crossing close to a corner used to get a hop squeezed into whatever space was left — as little as 2.9px against a requested 6px, opening exactly on the corner's tangent point. At that size the arc no longer clears the line it is meant to hop, so the two strokes still touch and the corner's curve runs straight into the arc's. It reads as a rendering fault rather than as a crossing.
Hops now keep a straight run clear of the bend, and one that would still have to shrink below 60% of the requested radius is dropped instead of drawn. An undrawn hop is an ordinary crossing, which is a much better failure than a broken-looking one.
This changes swimlane diagrams as well as ELK ones. Swimlanes are the existing consumer of line hops and have them on today, so a swimlane with a crossing near a bend will render differently even for someone not using ELK at all. It shows up wherever a layout stacks edges in narrow lanes: ELK routes subgraph-internal edges 10px apart, and 10px does not hold a 7.07px corner cut plus a 6px hop.
A crossing is also ignored now when it lands inside the stretch where either edge is rounding a bend. Crossings are found on polylines, but a rounded edge is not drawn as its polyline — it leaves the line up to 7.07px before each bend and rejoins it that far after. A crossing found inside that stretch is somewhere the stroke never goes, so the hop was arching over blank paper while the two lines carried on touching beside it.
-
#8146
def4c81Thanks @ashishjain0512! - fix(themes):redux-colorandredux-dark-colornow define every variable their base themes define, instead of silently falling back to untuned values forprimaryBorderColor,clusterBkg,clusterBorder,altBackground,compositeTitleBackgroundand the state and requirement edge-label backgrounds. Pie, gantt and user-journey also read the theme palette rather than a single pale tint, so pie slices are distinguishable and gantt sections are visibly banded. -
#8189
3802472Thanks @ashishjain0512! - fix(themes): venn circles follow theredux-colorandredux-dark-colorpalettes. Neither theme defined anyvenn*variable, so every circle rendered in one flat colour. -
#8185
f5af413Thanks @knsv-bot! - fix: sequence diagram actors under theneolook andreduxthemes. Notes no longer render bold, arectsection band is no longer drawn white on white, and participants follow one vertical model: every lifeline starts and ends on a shared line, all participant glyphs are one size with their feet on a common edge, single-line labels share one baseline in the header and in the mirrored footer, and a multiline label grows away from the lifeline instead of moving it. Theclassiclook is unchanged. -
#8157
a3a92baThanks @ashishjain0512! - fix(sequence): index each actor colour palette by its own length.Every actor-drawing call site read
bkgColorArray[actorCount % borderColorArray.length]— one palette indexed by the other's length. Underredux-colorboth palettes have twelve entries, so the wrong length happens to give the right answer and nothing is visibly broken today. It goes wrong as soon as the two differ in length: the overflow actors resolve toundefined, and d3 then strips the inline fill for some actors and not others.Both palettes now cycle within their own length, via a shared helper. An absent or empty palette still yields
undefinedrather than a substitute colour, which is whatredux-dark-colorrelies on: it ships a border palette and an empty background palette so actors are outlined but not filled. -
#8232
e691042Thanks @ashishjain0512! - Circle, double circle, Delay and Display nodes size themselves from the label's height as well as its width, so a wrapped label stays inside the outline. Each was sized from the width alone and clipped any label taller than it was wide, whichflowchart.wrappingWidth: 120made common.This replaces the wrapping exemption those shapes carried along with stadium and diamond: it stopped them wrapping at all, so a long label produced a single-line node hundreds of pixels wide. Stadium needs no exemption — its
w >= 1.5hfloor already keeps a wrapped label from closing the caps into a circle — and diamond never needed one.Stacked (
docs) and lined (lin-doc) document nodes lift their label clear of the wavy bottom edge by the wave's full depth rather than half of it, so the last line is no longer clipped by the trough. Plaindocalready did this; the two variants had copied it with the offset halved. Node dimensions are unchanged. -
#8154
ba0deedThanks @aloisklink! - fix: bundlefastdomto fix issues with globaldefine -
#8227
b05824aThanks @knsv-bot! - fix(flowchart): stop stadium labels from wrapping into a circle and exempt selected shapes from automatic label wrapping.Long stadium labels wrapped to
flowchart.wrappingWidth, grew taller without growing wider, and turned the stadium into a circle. Stadium now keeps its semicircular caps with an outer width of at least 1.5 times its height and enough clearance that the label stays inside the arcs.Stadium (
terminal,pill), circle (circ), diamond (diam,decision,question), double circle (dbl-circ,double-circle), Display (curv-trap,curved-trapezoid,display) and Delay (delay,half-rounded-rectangle) no longer wrap their labels atflowchart.wrappingWidth. Explicit line breaks are kept, and an explicit node width still takes precedence.Agentflow
decision(diamond) nodes are exempt in the same way and do not wrap atagentflow.wrappingWidth. -
#8192
0ec29e9Thanks @ashishjain0512! - fix(state): draw the edge between a state and its note dashed under theneolook. It was rendering solid.The dashes were expressed only as CSS —
.note-edge { stroke-dasharray: 5 }— which is enough underclassicbut not underneo, whereinsertEdgewrites an inlinestroke-dasharrayon every edge, computed from the path length so the arrow markers keep their gaps. An inline style outranks a stylesheet rule, so the note edge took the solid pattern and the.note-edgerule simply lost. The note edge now declarespattern: 'dashed', which is whatinsertEdgereads to choose its dash generator.classicis unchanged. -
#8225
0d77926Thanks @knsv-bot! - fix(swimlane): seed cycle removal from source nodes so feedback edges are reversed instead of forward flow edges. A cycle liketask --> decision --> fix --> taskno longer placesfixin the middle of thestart --> taskflow line. -
#8211
93edd72Thanks @ashishjain0512! - fix: keep association markers apart on use-case ovals under ELK. Ovals get the same vertical padding as horizontal, edge attachment points on them are spread across the side, and the ELK renderer now accounts for every end-marker offset (extension, circle, cross, …) so a short entry stub can no longer flip a marker around. -
Updated dependencies [
33442fd]:- @mermaid-js/parser@2.0.0
@mermaid-js/layout-elk@1.0.0
-
#8213
33442fdThanks @aloisklink! - chore!: require ES2024, Safari 17.4+, Node.JS v22.12+Mermaid is now built to target Safari 17.4+ and ES2024. If you need to support older browsers, you may need to polyfill or transpile mermaid.
Safari 17.4+ has been chosen as the floor, as unlike Firefox/Chrome, older iOS devices don't get major Safari updates.
Node.JS v22.12+ is also declared as requirement in our
package.jsonfiles, but as mermaid requires a browser, this is mainly so we can use dependencies that also declare a Node.JS v22.12+ requirement, without causing issues for users when runningnpm install.
-
#8152
d4bea0dThanks @knsv-bot! - feat:elk.presetpicks a named combination of the options that decide where nodes end up.Three options settle node positions, and they sit in different phases of the layout: which layer a node lands in, where it goes within that layer, and which edges get reversed to make the graph acyclic. Choosing them well means knowing all three interact;
presetnames the combinations worth using.default— network simplex layering, balanced Brandes-Koepf placement at the top level and inside subgraphs, and depth-first cycle breaking. Balanced placement centres branches and composite-state entries; depth-first breaking gives shorter back edges on graphs that loop.legacy— reproduces what earlier versions actually rendered: Brandes-Koepf placement with ELK's own greedy cycle breaking.modelOrder— asdepthFirst, but breaks cycles by greedy model order, which disturbs declaration order least at the cost of longer back edges.depthFirst— the previous default: network simplex layering and top-level placement, Brandes-Koepf placement inside subgraphs,NONEalignment, and depth-first cycle breaking.
--- config: layout: elk elk: preset: legacy ---
Setting
layeringStrategy,nodePlacementStrategy,nodePlacementAlignmentorcycleBreakingStrategyexplicitly overrides the preset for that one option and leaves the rest in place, so a preset is a starting point rather than a lock.Node placement keeps
BRANDES_KOEPF, but its alignment changes fromNONEtoBALANCEDand cycle breaking fromGREEDYtoDEPTH_FIRST, so existing ELK diagrams will lay out differently.preset: legacyrestores the previous behaviour, and is the single switch for it — this is the net change against the last release, measured from what shipped rather than from any intermediate state.Subgraph contents use
BRANDES_KOEPFunder every preset. FormodelOrderanddepthFirstthat is deliberately not the root's strategy: network simplex inside a frame produced routes that left the subgraph on its bounding-box corner. The two sides are tuned independently, sonodePlacementStrategyset explicitly still applies to both.Note that
legacyusesGREEDYcycle breaking rather than theGREEDY_MODEL_ORDERthe schema previously advertised. That default was declared in the schema but never listed in the shipped defaults, so it reached ELK as undefined and ELK's own default applied —legacyreproduces what was rendered, not what was documented. -
#8152
1246a55Thanks @knsv-bot! - feat: draw line hops where ELK edges cross, controlled byelk.lineHops.Where two edges cross, the one that gives way is drawn with a small arc (or a visible gap) so it is clear which line passes over which. On by default; set
elk.lineHops: falseto draw plain crossings, or'gap'to use gaps instead of arcs.--- config: layout: elk elk: lineHops: gap ---
The crossing detection and both styles already existed and were used by swimlanes — this registers the
afterPainthook that lets ELK use them. An edge that takes a hop loses its corner rounding on that segment, which is the trade for a readable crossing; curved edges are skipped rather than rewritten, to avoid corrupting their geometry.Existing ELK diagrams with crossing edges will render differently.
-
#8155
810893cThanks @ashishjain0512! - feat: this package is now built from mermaid's own ELK implementation instead of carrying its own copy, and is only needed for Mermaid builds that ship without ELK.mermaidbundles ELK and registers it automatically, so most projects can drop the dependency and theregisterLayoutLoaderscall. The package remains published and fully functional for the tiny build (mermaid.tiny.js), which omits ELK to stay small and where registering this is the only way to get an ELK layout.Because it is compiled from mermaid's ELK source rather than importing the whole
mermaidentry point, the published bundle no longer drags in every diagram type, parser and KaTeX: the minified ESM payload drops from roughly 1.58 MB to 728 kB gzipped. It stays self-contained, so it still loads from a CDN next to any Mermaid build with no import map.The rendering utilities the ELK source reaches (
dompurify,katex,dayjs,dagre-d3-es) are now declared in the package'sdependencies, so the npm build (.core.mjs) resolves them through your package manager — they dedupe against the host's copies and show up in audits — instead of carrying invisible inlined copies.Maintainers:
peerDependencies.mermaidstill reads^11.0.2. It should be raised to the major that bundles ELK as part of the release.
-
#8152
785ca77Thanks @knsv-bot! - fix: draw ELK subgraph frames an even distance from their contents.A subgraph could sit 76px from its nodes on one side and 24px on the other, with nothing visible in the gap. ELK sizes a container around everything it put inside, edges included, and an edge that runs against the flow of the layout is routed back around the outside — so a group holding one grew on whichever side that edge left by, and a group without one did not.
The lane is real and the edge still needs it, so the space is not reclaimed. What changes is that the frame is no longer drawn around it: the frame is pulled in to an even distance from the group's own children, and the edge keeps its lane just outside, which is what an edge routed around a group should look like anyway.
The top is left as ELK set it, since it carries the subgraph's title strip and there is no way to tell how much of that padding is the title and how much is spare.
A frame still holds the lanes that genuinely belong to it. An edge with both endpoints inside a group never leaves it, so its lane is part of that group's interior and the frame stays drawn around it — which matters for nested groups, where a lane routed around an inner group sits inside the outer one.
Subgraphs render tighter, and groups that used to be visibly lopsided are now even.
-
#8228
e36b883Thanks @knsv-bot! - Reserve subgraph title padding before ELK routes edges so attachments stay on the painted frame. Clip stale interior endpoints along the incoming segment, avoiding edges that run along the subgraph border. -
#8152
1befa91Thanks @knsv-bot! - fix: edges leave diamonds, stadiums and other non-rectangular shapes without kinking.ELK routes to ports on a node's bounding box and always leaves one perpendicular to the side it sits on. For a rectangle that port is the attachment point; for anything else the outline is inside the box, so the attachment has to move inwards — and the direction it moves in decides whether the edge stays orthogonal.
It used to move along the ray from the node's centre, which lands on the outline at a different offset along the side than the port ELK chose, so the opening segment came out diagonal. The attachment now walks the outline along the edge's own departure axis, staying collinear with ELK's stub: the edge leaves the outline, crosses the box, and carries on in one straight line. Rectangular nodes are unaffected.
-
#8152
1348b4fThanks @knsv-bot! - fix: an edge no longer leaves a subgraph from the frame's corner.elk.spacing.portsSurroundingwas left at ELK's default of0, which permits a port to sit exactly on a node's corner. A corner is the one boundary point with no side to leave from, so the edge came out of the vertex and then ran ALONG the frame's own edge before turning away from it. Subgraphs showed it first, because an edge that crosses a subgraph boundary attaches to the frame rather than to a node inside it, and a frame is large enough for the result to be obvious.A margin is now reserved at the ends of every side, so ELK keeps ports off the corners itself rather than the renderer correcting them afterwards. Over the
elk-edge-casescorpus this takes the fixtures with a corner endpoint from 8 of 30 down to 3.The value is 12, chosen by measurement: it is the smallest that clears the corner on that corpus. It is not a free parameter — 30 was tried and reorders layers.
-
#8194
012e1f7Thanks @knsv-bot! - fix: center edges attached to small nodes such as start/end state circles. ELK's ports-surrounding margin exceeded the side length of nodes narrower than 24px, parking the edge anchor off-center; such anchors are now discarded so the edge aims at the node center. -
#8152
1befa91Thanks @knsv-bot! - fix: an edge no longer leaves a node with a tiny kink.ELK spreads an edge's port evenly along a node's side, then routes the edge down a channel whose row rarely lines up with that port exactly. The leftover is a staircase of a few pixels right at the border: leave the port, run a short distance, step perpendicular onto the channel, carry on. With rounded corners the two micro-bends sit on top of each other and read as a glitch — one edge in the sample corpus stepped 3.25px and rendered as two quadratic curves with a zero-length segment between them.
The step is now removed by moving the channel onto the port's row, so the edge draws as one straight line and both ports stay exactly where the layout put them — sliding an attachment along a node's border leaves a node whose other edges are still evenly spread looking lopsided. Only a step next to a node is touched, and only when it is small and the edge continues the same way afterwards, so a real turn is never collapsed. An edge is left alone entirely when moving its run would drag the far port, or would buy a crossing.
Set
elk.straightenEdges: falseto keep the previous behaviour. -
#8152
3b09225Thanks @knsv-bot! - fix: stop ELK subgraphs padding one side more than the other.A subgraph could end up with far more space on one side than the other for no reason a reader could see — 74px on the right of one group against 24px everywhere else. The extra space was a routing lane held open for an edge that runs against the flow of the layout and has to be routed back around, and its width came from
spacing.baseValue.That base value was doing two jobs at once. Every unset ELK spacing derives from it, so it had to stay large enough that an edge got a straight run before the node it enters — below about 40 the approach came out shorter than the arrowhead and the turn read as happening underneath it. But an edge routed down the inside of a frame claims a lane the same width, so paying for the approach out of the base value also pushed groups clear of their own borders.
The two are now bought separately. The base value drops to 24, and the approach run, node separation and edge separation are set explicitly, so edges keep the run they had without the frame paying for it.
elk.layered.spacing.edgeNodeBetweenLayersis the key that buys the approach. An earlier attempt usedelk.layered.spacing.edgeEdgeBetweenLayers, which is edge-to-edge and a different quantity, and a note in the source concluded from it that ELK ignored edge-node spacing "in every key form". It does not; that note was wrong and is corrected.Subgraph contents also gain
PORT_POSITIONnode flexibility, which lets a node shift so an edge can leave straight rather than bending off the port. (Their placement strategy is covered in theelk.presetnote.)Existing diagrams with subgraphs will render differently — groups get tighter and more even.
-
#8129
83f5c47Thanks @ashishjain0512! - fix(build): externalizepeerDependenciesin core builds so the layout plugins no longer inline a second copy of mermaidgetBuildConfigonly externalizeddependencies, so a runtime (non-type) import of the peer-depended mermaid resolved throughexportstodist/mermaid.core.mjsand esbuild inlined the whole bundle.@mermaid-js/layout-elk's core entry had grown to 106 files / 6.6 MB, carrying its own mermaid with separate module-level singletons — so mermaid rendering fixes did not reach the ELK layout path until the plugin itself was republished. The core entry is back to 3 files / ~41 KB and now defers to the host's mermaid. The self-containedesmentry is unchanged. -
Updated dependencies [
7a3c1a8,0cf3797,846fd65,33442fd,dc2e453,ce0302d,ad98070,e2aab3f,6b79518,30325d4,cd48a64,c3ee3c7,1ee934d,a7831c5,0cf3797,0320406,810893c,e36b883,b993915,4e00c5c,3f05015,d57ed55,1fc5bb3,b979eb4,7ee4c3f,a6ec7ff,1246a55,813c766,8603bdd,43d9fbc,2878cf3,16b9a7d,def4c81,3802472,a19bd08,f5af413,a3a92ba,e691042,ba0deed,b05824a,0ec29e9,0d77926,93edd72,75e6c30,6df1149,a31ecb7]:- mermaid@12.0.0
@mermaid-js/parser@2.0.0
-
#8213
33442fdThanks @aloisklink! - chore!: require ES2024, Safari 17.4+, Node.JS v22.12+Mermaid is now built to target Safari 17.4+ and ES2024. If you need to support older browsers, you may need to polyfill or transpile mermaid.
Safari 17.4+ has been chosen as the floor, as unlike Firefox/Chrome, older iOS devices don't get major Safari updates.
Node.JS v22.12+ is also declared as requirement in our
package.jsonfiles, but as mermaid requires a browser, this is mainly so we can use dependencies that also declare a Node.JS v22.12+ requirement, without causing issues for users when runningnpm install.
electron v43.7.0
- Added
initiatorOriginto webRequest and protocol handler requests,DownloadItem.getInitiatorOrigin(), and a trailingframeargument to thewill-downloadandpreconnectsession events. #53704 (Also in 44, 45) - Added the
ELECTRON_DEBUG_DRAGGABLE_REGIONSenvironment variable, which visualizes and logs draggable regions in unpackaged apps to help debug custom title bars. #53633 (Also in 44, 45)
- File System Access permission requests and the
file-system-access-restrictedevent are scoped to the requesting document, grants are reset when the origin's last page closes, and write access works in in-memory sessions. #53694 (Also in 42, 44, 45) - Fixed
session.setPermissionCheckHandlerreceiving the top-level origin and a nullwebContentsforhidandusbchecks made from a subframe. #53689 (Also in 42, 44, 45) - Fixed
webContents.on(),removeListener()andremoveAllListeners()throwing "Object has been destroyed" forconsole-messagelisteners after the WebContents was destroyed. #53496 (Also in 42, 44, 45) - Fixed a crash on Linux when
process.envwas written while another thread was reading the environment, and a memory leak when a worker thread exits. #53510 (Also in 42, 44, 45) - Fixed a possible crash on Windows when a file dialog was shown for a window that was being closed at the same time. #53584 (Also in 42, 44, 45)
- Fixed a renderer crash when the main process sent IPC to, or a page navigated, a same-process
window.open()child whosecontextIsolationdiffered from its opener's. #53540 (Also in 42, 44, 45) - Fixed an issue where the browser-side check that blocks drag and drop between cross-site frames of the same page was disabled. #53750 (Also in 44, 45)
- Fixed crashes in
setDisplayMediaRequestHandlerwhen the granted frame had been destroyed or another tab was granted by id. #53671 (Also in 42, 44, 45) - Fixed several non-functional DevTools integrations: the Security panel's "View certificate" button, Ctrl+wheel zooming inside DevTools, and DevTools keyboard shortcuts (e.g. F8) while the inspected page has focus. #53631 (Also in 44, 45)
- Internal
<webview>,window.close()andexecuteJavaScriptreply IPCs are validated against the sending frame. #53726 (Also in 42, 44, 45) <webview>withoutallowpopupsalso blocks links opened into a new window by modifier-click, and such windows navigate as the clicking document rather than as a browser-initiated load. #53718 (Also in 42, 44, 45)getUserMediawithchromeMediaSource: 'desktop'no longer accepts WebContents source ids; usechromeMediaSource: 'tab'withwebContents.getMediaSourceId()orsetDisplayMediaRequestHandlerto capture a WebContents. #53707 (Also in 42, 44, 45)nodeIntegrationInWorkernow applies only to workers created by frames that themselves have Node integration; enablenodeIntegrationInSubFramesto keep Node in workers created by subframes. #53712 (Also in 42, 44, 45)openExternalpermission requests started by a frame that has since gone away are attributed to that frame's origin rather than to the navigating page. #53698 (Also in 42, 44, 45)pointerLockandkeyboardLockpermission requests now report the requesting frame, andexecCommand('paste')requires user activation in the frame that calls it. #53701 (Also in 42, 44, 45)
- Backported fixes from upstream ANGLE, Chromium, libyuv, PDFium and V8. #53766
- Fixed a renderer crash that could occur when printing from an iframe. #53761 (Also in 44, 45)
- Fixed the app becoming unresponsive when a page has a very large number of
app-regiondraggable elements. #53632 (Also in 44, 45) - Updated Node.js to v24.21.0. #53777
react-intl: 10.1.27
- fix(formatjs_cli): extract messages from tagged template literals by @vpontis in https://github.com/formatjs/formatjs/pull/7182
- docs: track ECMA-402 draft polyfill audit by @longlho in https://github.com/formatjs/formatjs/pull/7185
- docs: link ECMA-402 audit fixes and validation by @longlho in https://github.com/formatjs/formatjs/pull/7200
- codex/ecma402 coercion by @longlho in https://github.com/formatjs/formatjs/pull/7186
- fix(@formatjs/intl-supportedvaluesof): coerce keys before lookup by @longlho in https://github.com/formatjs/formatjs/pull/7187
- fix(@formatjs/intl-listformat): follow the string iterator protocol by @longlho in https://github.com/formatjs/formatjs/pull/7189
- fix(deps): negotiate well-formed numbering system options by @longlho in https://github.com/formatjs/formatjs/pull/7190
- fix(@formatjs/intl-collator): validate collation and comparison inputs by @longlho in https://github.com/formatjs/formatjs/pull/7191
- fix(@formatjs/intl-datetimeformat): reject offsets containing seconds by @longlho in https://github.com/formatjs/formatjs/pull/7192
- fix(@formatjs/intl-displaynames): validate and canonicalize display codes by @longlho in https://github.com/formatjs/formatjs/pull/7193
- fix(@formatjs/intl-durationformat): validate duration records exactly by @longlho in https://github.com/formatjs/formatjs/pull/7194
- fix(@formatjs/intl-locale): canonicalize weekday and numeric options by @longlho in https://github.com/formatjs/formatjs/pull/7195
- fix(@formatjs/intl-locale): align week info with ECMA-402 by @longlho in https://github.com/formatjs/formatjs/pull/7196
- fix(@formatjs/intl-pluralrules): align draft options and ranges by @longlho in https://github.com/formatjs/formatjs/pull/7197
- fix(@formatjs/intl-segmenter): preserve iterator progress and locale by @longlho in https://github.com/formatjs/formatjs/pull/7198
- fix(@formatjs/intl-getcanonicallocales): follow locale-list coercion by @longlho in https://github.com/formatjs/formatjs/pull/7199
- fix(@formatjs/intl-numberformat): accept callable option objects by @longlho in https://github.com/formatjs/formatjs/pull/7201
- fix(@formatjs/intl-segmenter): reject coerced BigInt indices by @longlho in https://github.com/formatjs/formatjs/pull/7202
- test(@formatjs/intl-numberformat): enforce Test262 result accounting by @longlho in https://github.com/formatjs/formatjs/pull/7209
- test(@formatjs/intl-numberformat): cover complete current Test262 suites by @longlho in https://github.com/formatjs/formatjs/pull/7210
- fix(@formatjs/intl-numberformat): omit NaN sign with exceptZero by @longlho in https://github.com/formatjs/formatjs/pull/7212
- test: use generated Test262 harness rules by @longlho in https://github.com/formatjs/formatjs/pull/7216
- fix(@formatjs/intl-numberformat): reject undefined range endpoints by @longlho in https://github.com/formatjs/formatjs/pull/7226
- fix(@formatjs/intl-numberformat): preserve Symbol coercion errors by @longlho in https://github.com/formatjs/formatjs/pull/7227
- codex/test262 numberformat option validation by @longlho in https://github.com/formatjs/formatjs/pull/7228
- fix(@formatjs/intl-listformat): preserve language data when loading root locale by @longlho in https://github.com/formatjs/formatjs/pull/7229
- fix(@formatjs/intl-locale): enforce internal receiver branding by @longlho in https://github.com/formatjs/formatjs/pull/7231
- fix(@formatjs/intl-collator): match built-in function descriptors by @longlho in https://github.com/formatjs/formatjs/pull/7232
- fix(@formatjs/intl-numberformat): make built-in methods non-constructible by @longlho in https://github.com/formatjs/formatjs/pull/7233
- fix(@formatjs/intl-numberformat): account for significant precision in rounding magnitude by @longlho in https://github.com/formatjs/formatjs/pull/7234
- fix(@formatjs/intl-numberformat): validate internal receiver branding by @longlho in https://github.com/formatjs/formatjs/pull/7235
- fix(@formatjs/intl-datetimeformat): truncate fractional timestamps toward zero by @longlho in https://github.com/formatjs/formatjs/pull/7236
- fix(@formatjs/intl-datetimeformat): match built-in method metadata by @longlho in https://github.com/formatjs/formatjs/pull/7238
- test(deps): run Test262 with stable Temporal support by @longlho in https://github.com/formatjs/formatjs/pull/7240
- fix(@formatjs/intl-durationformat): match built-in metadata and option order by @longlho in https://github.com/formatjs/formatjs/pull/7241
- fix(@formatjs/intl-durationformat): display the duration sign once by @longlho in https://github.com/formatjs/formatjs/pull/7242
- fix(@formatjs/intl-durationformat): preserve exact numeric duration formatting by @longlho in https://github.com/formatjs/formatjs/pull/7243
- fix(@formatjs/intl-durationformat): propagate numeric unit styles by @longlho in https://github.com/formatjs/formatjs/pull/7244
- fix(@formatjs/intl-durationformat): retain zero minutes between numeric fields by @longlho in https://github.com/formatjs/formatjs/pull/7245
- fix(@formatjs/intl-localematcher): preserve supported requested locale tags by @longlho in https://github.com/formatjs/formatjs/pull/7247
- fix(@formatjs/intl-supportedvaluesof): make the built-in non-constructible by @longlho in https://github.com/formatjs/formatjs/pull/7248
- fix(@formatjs/intl-numberformat): preserve resolved option property order by @longlho in https://github.com/formatjs/formatjs/pull/7249
- fix(@formatjs/intl-relativetimeformat): isolate internal formatter options by @longlho in https://github.com/formatjs/formatjs/pull/7250
- fix(@formatjs/intl-localematcher): isolate locale resolution records by @longlho in https://github.com/formatjs/formatjs/pull/7251
- fix(@formatjs/intl-locale): use standard locale tag coercion by @longlho in https://github.com/formatjs/formatjs/pull/7252
- fix(@formatjs/intl-locale): support the variants constructor option by @longlho in https://github.com/formatjs/formatjs/pull/7253
- fix(@formatjs/intl-locale): canonicalize before applying locale overrides by @longlho in https://github.com/formatjs/formatjs/pull/7254
- fix(@formatjs/intl-locale): minimize maximized locale components by @longlho in https://github.com/formatjs/formatjs/pull/7255
- feat(@formatjs/editor): add headless React 19 editor with StyleX UI by @longlho in https://github.com/formatjs/formatjs/pull/7223
- fix(@formatjs/intl-locale): honor region preference priority by @longlho in https://github.com/formatjs/formatjs/pull/7257
- fix(@formatjs/intl-locale): expose canonical option values by @longlho in https://github.com/formatjs/formatjs/pull/7258
- fix(@formatjs/intl-locale): follow likely-subtag lookup order by @longlho in https://github.com/formatjs/formatjs/pull/7259
- fix(@formatjs/intl-durationformat): resolve supported numbering systems by @longlho in https://github.com/formatjs/formatjs/pull/7260
- fix(@formatjs/intl-durationformat): accept Temporal duration inputs by @longlho in https://github.com/formatjs/formatjs/pull/7261
- fix(@formatjs/intl-displaynames): accept extended language subtags by @longlho in https://github.com/formatjs/formatjs/pull/7262
- fix(@formatjs/intl-supportedvaluesof): include fixed-offset time zones by @longlho in https://github.com/formatjs/formatjs/pull/7263
- fix(@formatjs/intl-collator): preserve locale punctuation defaults by @longlho in https://github.com/formatjs/formatjs/pull/7264
- fix(@formatjs/intl-collator): exclude root from locale negotiation by @longlho in https://github.com/formatjs/formatjs/pull/7265
- fix(@formatjs/intl-localematcher): respect Unicode extension boundaries by @longlho in https://github.com/formatjs/formatjs/pull/7266
- fix(@formatjs/intl-collator): resolve canonical collation types by @longlho in https://github.com/formatjs/formatjs/pull/7267
- fix(@formatjs/intl-collator): select search collation tailoring by @longlho in https://github.com/formatjs/formatjs/pull/7268
- feat(@formatjs/editor): add headless translation workflow by @pgonsolin in https://github.com/formatjs/formatjs/pull/7157
- fix(@formatjs/intl-supportedvaluesof): probe locale-specific collations by @longlho in https://github.com/formatjs/formatjs/pull/7269
- fix(@formatjs/intl-datetimeformat): handle exact date range boundaries by @longlho in https://github.com/formatjs/formatjs/pull/7270
- fix(@formatjs/intl-datetimeformat): validate receiver internal slots by @longlho in https://github.com/formatjs/formatjs/pull/7271
- fix(@formatjs/intl-datetimeformat): align option property metadata by @longlho in https://github.com/formatjs/formatjs/pull/7272
- fix(@formatjs/intl-datetimeformat): format year zero in the BC era by @longlho in https://github.com/formatjs/formatjs/pull/7273
- fix(@formatjs/intl-datetimeformat): normalize timezone inputs by @longlho in https://github.com/formatjs/formatjs/pull/7274
- fix(@formatjs/intl-datetimeformat): isolate option records by @longlho in https://github.com/formatjs/formatjs/pull/7275
- fix(@formatjs/intl-datetimeformat): support all hour-cycle options by @longlho in https://github.com/formatjs/formatjs/pull/7276
- fix(@formatjs/intl-datetimeformat): select independent hour12 preferences by @longlho in https://github.com/formatjs/formatjs/pull/7277
- fix(@formatjs/intl-getcanonicallocales): canonicalize locale extensions by @longlho in https://github.com/formatjs/formatjs/pull/7278
- fix(@formatjs/intl-getcanonicallocales): resolve subdivision aliases by @longlho in https://github.com/formatjs/formatjs/pull/7279
- fix(@formatjs/intl-getcanonicallocales): apply compound language aliases by @longlho in https://github.com/formatjs/formatjs/pull/7280
- fix(@formatjs/intl-getcanonicallocales): append list elements directly by @longlho in https://github.com/formatjs/formatjs/pull/7281
- codex/editor es2016 compatibility by @longlho in https://github.com/formatjs/formatjs/pull/7286
- test(deps): cover combined polyfill installation by @longlho in https://github.com/formatjs/formatjs/pull/7282
- test(deps): load required Test262 locale fixtures by @longlho in https://github.com/formatjs/formatjs/pull/7284
- fix(@formatjs/intl-getcanonicallocales): preserve RegExp statics by @longlho in https://github.com/formatjs/formatjs/pull/7287
- fix(@formatjs/intl-displaynames): use canonical calendar data keys by @longlho in https://github.com/formatjs/formatjs/pull/7288
- fix(@formatjs/intl-numberformat): isolate internal plural options by @longlho in https://github.com/formatjs/formatjs/pull/7290
- fix(@formatjs/intl-numberformat): honor locale grouping minimums by @longlho in https://github.com/formatjs/formatjs/pull/7291
- fix(@formatjs/intl-datetimeformat): reuse parsed pattern fields by @longlho in https://github.com/formatjs/formatjs/pull/7292
- fix(@formatjs/intl-datetimeformat): match hourless time requests by @longlho in https://github.com/formatjs/formatjs/pull/7293
- fix(@formatjs/intl-datetimeformat): support ISO calendar negotiation by @longlho in https://github.com/formatjs/formatjs/pull/7294
- fix(@formatjs/intl-pluralrules): generate compact exponent data by @longlho in https://github.com/formatjs/formatjs/pull/7295
- fix(formatjs_cli): preserve IDs for empty descriptions by @longlho in https://github.com/formatjs/formatjs/pull/7296
- fix(@formatjs/intl-datetimeformat): compare ranges at displayed precision by @longlho in https://github.com/formatjs/formatjs/pull/7297
- fix(@formatjs/intl-datetimeformat): format flexible day periods by @longlho in https://github.com/formatjs/formatjs/pull/7298
- fix(deps): preserve polyfill locale data during registration by @longlho in https://github.com/formatjs/formatjs/pull/7299
- fix(@formatjs/intl-segmenter): register CLDR locale coverage by @longlho in https://github.com/formatjs/formatjs/pull/7300
- fix(@formatjs/intl-numberformat): preserve range separators and affixes by @longlho in https://github.com/formatjs/formatjs/pull/7301
- fix(@formatjs/intl-numberformat): place approximate signs in locale patterns by @longlho in https://github.com/formatjs/formatjs/pull/7302
- fix(@formatjs/intl-numberformat): share signs with range unit suffixes by @longlho in https://github.com/formatjs/formatjs/pull/7303
- fix(@formatjs/intl-numberformat): repair benchmark entrypoints by @longlho in https://github.com/formatjs/formatjs/pull/7304
- fix(@formatjs/intl-numberformat): preserve supplementary digits when grouping by @longlho in https://github.com/formatjs/formatjs/pull/7305
- fix(@formatjs/intl-numberformat): generate complete CLDR digit mappings by @longlho in https://github.com/formatjs/formatjs/pull/7306
- feat(@formatjs/editor): publish headless editor package by @longlho in https://github.com/formatjs/formatjs/pull/7285
- fix(deps): consolidate polyfill conformance and performance fixes by @longlho in https://github.com/formatjs/formatjs/pull/7320
- fix(@formatjs/editor): bootstrap the headless editor release by @longlho in https://github.com/formatjs/formatjs/pull/7321
- @pgonsolin made their first contribution in https://github.com/formatjs/formatjs/pull/7157
Full Changelog: https://github.com/formatjs/formatjs/compare/react-intl@10.1.26...react-intl@10.1.27