DreamShaderLang
ChangeLog

DreamShader Plugin

Release notes for the DreamShader Unreal plugin, newest first, taken from the plugin's own CHANGELOG.

Release notes for the Unreal plugin, newest first. Entries record what changes for you — the language surface, the generator, the editor tooling and engine compatibility.

Current release1.8.0 — 2026-08-21
EnginesUnreal Engine 5.3 through 5.8, Win64
Source of recordthe plugin's own CHANGELOG.md, shipped in the plugin folder

1.8.0 - 2026-08-21

The release about not losing things. Generation used to tear an asset down unconditionally: a hand-edited asset was overwritten silently, a failed rebuild left the asset empty, and an asset left open in the material editor had the rebuild quietly reverted by the next Apply. This version closes all of those — an output fingerprint, an atomic rebuild, an open-editor gate, dependency-ordered batches, and a build key that finally covers everything deciding what a source compiles into.

Divergence — three ways out

  • Every successful generation stamps a DreamShader.OutputDigest fingerprint of what the asset actually holds, and the next rebuild compares it before anything is cleared. A mismatch fails the compile with the asset exactly as you left it — not cleared, not half-built. See Regeneration.
  • The digest covers exactly what a rebuild would destroy: nodes, connections, node properties, the reset-property set, a function's asset-level fields, and a ThinCustom instance's parameter overrides. It deliberately excludes what a rebuild never touches — node positions, comment boxes you wrote, pin GUIDs, and properties outside the reset list. Blocking on a property that was never in danger only refuses a rebuild for nothing.
  • All three ways out are on the asset's right-click menu under DreamShader: Revert to Source (discard the edits and rebuild), Adopt Into Source (rewrite the .dsm / .dsf from the asset, backing the old one up to <source>.bak), and Detach From DreamShader (keep the asset, stop managing it).
  • -Force does not get past it. bForce answers "is the source hash stale", and the editor asserts it for every file in its own startup sweep — honouring it would have left the gate dead in the mode the editor spends all its time in. Only Revert overrides a divergence, because only a person can make that call.
  • Memory-only assets are stamped too, with the digest and the source path (but not the source hash, which would switch the skip on). Without the path an in-memory asset classifies as foreign and the gate never fires — dead in the editor's default mode.

A rebuild is atomic

  • The steps either all take effect or none of them do. The old graph is now detached rather than destroyed, and destroyed only once the rebuild has fully succeeded; a failure restores the asset from a serialized snapshot — render state, material-function usage, node graph, connections and FunctionInput / FunctionOutput pin GUIDs all come back, so existing call sites stay wired.
  • This was necessary because not every failure can be caught up front: the whole-file parse, the Settings validation and the Outputs validation all run before the asset is touched, but a Graph block is compiled one statement at a time by the graph builder, which runs after the teardown. Before 1.8.0 such a failure left the asset emptied — bad for a material, much worse for a material function, whose call sites read their pins from the live asset: one bad .dsf took every material that called it down with it, with no undo (generated assets are deliberately not RF_Transactional).
  • Two things fall out of it. The teardown's two deletion strategies collapsed into one — the node-by-node path existed to break inbound links, which is pointless when the graph leaves as a unit — so the former 1200-expression threshold is gone and every rebuild takes the fast path. And DependentFunctionExpressionCandidates, the second serialized node list whose stale entries used to arm a null-deref for the rest of the session after a failed function compile, is now reset at commit rather than at teardown.

A rebuild is refused while the asset is open in an asset editor

  • An asset editor does not edit the asset: FMaterialEditor duplicates it into a transient UPreviewMaterial and copies that duplicate back over the original on Apply or Save; the material instance editor writes back through a UMaterialEditorInstanceConstant wrapper. An editor left open across a rebuild is therefore holding a pre-rebuild copy, and the next Apply silently reverts everything the rebuild did — surfacing later as a divergence report on the compile after that, a long way from the cause.
  • Compiles now stop with Asset '{ObjectPath}' is open in an asset editor, so it was NOT rebuilt. … and say to close it.
  • Revert to Source and Adopt Into Source are the exception: they close the editor themselves, act, and reopen it. Both are offered on that editor's own toolbar, and a menu item that is permanently dead where it is most useful is not a guard, it is a bug.

The build key replaces the plain source hash

  • DreamShader.SourceHash is now a build key: prepared source text, the Default Compiler Backend, the three mapping tables, the plugin version plus a hand-bumped format tag, and the engine version. It used to hash the source text alone, so anything else that changes the output left every already generated asset looking current.
  • The forced full sweep that was bolted onto the backend setting is therefore gone: each affected asset fails the skip check on its own, and one the setting does not affect is still skipped instead of being needlessly rebuilt.
  • Changing the key's composition invalidates every existing stamp — one rebuild per asset, once, which is the intended effect.

A batch of source files compiles in dependency order

  • A .dsm that calls a ShaderFunction binds its call node against the live UMaterialFunction asset — SetMaterialFunction reads the pins off the object, not off the source — so compiling the caller first bound it against the previous version of that function's interface. Renaming a function input and saving both files was enough to hit it, and which one won depended on the iteration order of a TMap.
  • Both drain points — the watcher's pending-file batch and the whole-project sweep — now topologically sort by the import graph. Only edges inside the batch are honoured; a cycle is left for the import loader to reject with its own diagnostic.

One editor owns the bridge for a project

  • The bridge directory is per project, not per process: one Requests folder, one status.json, one heartbeat. Two editors open on the same project therefore both polled the same queue and both overwrote status.json with their own pid.
  • Ownership is now a lock file, Bridge/owner.lock, carrying the owning pid and a heartbeat: an owner is believed while its process is alive and its heartbeat is under 30s old. Both tests are needed — the pid test alone hands the bridge over whenever the owner is mid-compile (a compile blocks the game thread), and the heartbeat test alone leaves it unowned after a hard crash. It is released on shutdown.
  • A non-owning editor still compiles its own in-memory materials, but does not consume requests, write status.json, or write generated assets to disk. That last part follows directly from storage deciding how a rebuild persists: without it, two editors would both SavePackage the same file, and a save that loses that race is not a merge — it is a corrupted package or a dead editor. The commandlet is unaffected. See Editor Tools.

An asset that exists on disk is rebuilt on disk

  • Storage decides how a rebuild persists, not the compile that asked for it. The editor asks for a memory-only compile every time, and when that landed on a package which already existed, generation rebuilt it in place and then cleared the dirty flag — producing an object that matched neither the file on disk nor anything that would ever be written, and that reported itself clean. A Save All could persist a state nobody chose, and the version visible in the editor disappeared on restart. The only signal was one log warning.
  • IsGeneratedAssetPersisted now decides: an asset with a file behind it takes the persisted path — stamped, saved, dirty flag never faked — and one without stays memory-only. There is exactly one answer to "what is this asset" again. See In-memory Materials.
  • The startup sweep stopped forcing as part of this, and had to. Forcing was free while every in-memory asset regenerated regardless of its source hash; it stopped being free the moment a disk-backed asset started being saved, because every editor launch would then rewrite every persisted generated asset. Changing the Default Compiler Backend still forces, because the hash cannot see it.

Fixed

  • A Domain="Volume" material carries bUsedWithVolumetricCloud, and decompiling one no longer turns the flag back on. The flag is now derived from the domain, and an explicit bUsedWithVolumetricCloud = "false"; still wins — a Volume material that only feeds volumetric fog can decline the cloud shader permutations. It is written through UMaterial::SetUsageByFlag (UE 5.8 deprecates every bUsedWith* field), and the assignment moved out of the Domain branch to read the domain back off the material, because ResetMaterialToDefaults does not clear usage flags. The decompiler goes through the same GetDefaultUsedWithVolumetricCloud, so UMaterial.dsmUMaterial is an identity again. See Material Settings. Reported in #27.
  • A swizzle survives the material editor. A component selection like CustomStencil.r was written as an inline mask on the connection (OutputIndex=0 plus Mask/MaskR), which the HLSL translator honours but the material graph editor cannot draw: pins correspond to an expression's Outputs entries, and no pin means "output 0, red channel only". UMaterialGraph::GetValidOutputIndex distrusts OutputIndex 0 whenever a mask is present, finds no matching pin, and falls back to the node's last output — which the first Apply/Save then writes back. On a SceneTexture node (Color / Size / InvSize) that turned CustomStencil.r into InvSize, silently, with no error anywhere: the stencil comparison failed for every pixel and the effect the function gated simply stopped appearing. Any generated asset touched through the material editor was exposed, and duplicating one first did not help. Component selections are now emitted in a form the graph can round-trip. An inline mask is kept only where the editor resolves it back to the pin it already names; where the expression publishes the same value through several masked outputs (TextureSample's RGB/R/G/B/A/RGBA, VertexColor, …) the matching output is named directly; otherwise a real ComponentMask node is emitted. Mixed output sets like SceneTexture's are never retargeted, because there the outputs are different values rather than views of one. The build key tag moves to DSK2, so every generated asset is rebuilt once into the new form.
  • UE.CollectionParam(...) Name; in Properties is as wide as the collection parameter. The parser records the declaration form as a scalar (it cannot open the collection), and the generator used that width as-is, so a vector MPC parameter passed to a float4 input was widened by three AppendVector splats and the material failed with Can't append float4 to float4. The width now comes from the loaded collection (vector → 4, scalar → 1); an explicit OutputType= still wins. See Properties.
  • A render target is accepted where a texture of its dimension is expected. The dimension check compared asset classes; it now reads UTexture::GetMaterialType() — the same answer the material compiler gives on a texture-object pin — so UTextureRenderTargetVolume, UTextureRenderTarget2D, UTextureRenderTargetCube and UTextureRenderTarget2DArray pass for their dimension, and anything else a texture-object pin would take does too.
  • Cook-generated assets are registered with the AssetRegistry, so they reach the package. A cook request is resolved through IAssetRegistry::DoesPackageExistOnDisk, which consults only the registry's in-memory state and has no filesystem fallback; generation runs on post-engine-init, after the registry enumerated the content directories, so a freshly written package was invisible to that lookup — the material simply was not in the pak, and LoadObject failed at runtime in a Shipping build, with no error anywhere. Generation now records what its saves actually wrote and hands the filenames to IAssetRegistry::ScanModifiedAssetFiles before the commandlet's Main collects the initial requests. Reported in #26.
  • The ThinCustom instance path runs the ownership guard. It checked only the class of whatever sat at the target path, not its provenance, so generating onto a hand-authored UDreamShaderMaterialInstance adopted it and then cleared its parameter overrides. Since ThinCustom is the default backend this was the widest of the three creation paths and the only one without the check.
  • DreamShaderGraphDecompilerHelpers.h compiles on its own — it declared functions taking EMaterialDomain and EBlendMode without including either enum's header, which a unity build only ever had through a neighbouring translation unit.

Added

  • #include at the top of a Function body is hoisted to file scope. A Function block's HLSL used to be emitted verbatim between the braces of the generated DreamShaderFn_* definition, which made any #include land inside a function — fine for macro-only headers, a compile error for anything that defines a function. Leading #include "…" / #include <…> directives (only whitespace and comments before them) are now stripped from the body and emitted once, in first-seen order, right after the guard of the generated .ush; a SelfContained embed or a GraphFunction body puts them on the Custom node's IncludeFilePaths instead, ahead of the generated include. A directive after the first statement keeps the old behaviour. This is what lets a header shared between C++ and HLSL be consumed from a .dsf without copying its functions into DreamShaderLang. See Functions.

1.7.1 - 2026-08-16

Fixed

  • Closing the editor crashed after using a Graph breakpoint. An access violation reading 0x3b0 in UMaterial::GetExpressionInputDescription, from the probe preview's own destructor, reached through FDreamShaderPreviewWebSocketServer::Shutdown while modules were unloading. Modules unload from inside the exit path: EngineExit() raises the exit request, FEngineLoop::Exit() runs the purge, and only then calls UnloadModulesAtShutdown() — so a module tearing down its own state at that point is doing it after the objects it points at are gone. TStrongObjectPtr keeps the preview material out of the garbage collector's reachable-set sweep, but it does not exempt it from the exit purge: the pointer stayed non-null while the object behind it did not, which is why the existing null checks passed and the dereference still faulted. What it faulted on is worth naming, because nothing about the call site suggests it — UMaterial::GetExpressionInputDescription and GetExpressionCollection both dereference GetEditorOnlyData() without checking it, so reaching for a material's inputs or expressions after the purge is an unconditional null-plus-offset read rather than a recoverable failure. The teardown is now skipped once the engine is exiting, which is correct and not merely safe: releasing the shared expression collection exists so the preview material cannot touch the graph material's nodes later, and at exit there is no later. The ordinary path, where a client disconnects while the editor keeps running, is unchanged.

1.7.0 - 2026-08-16

Added

  • Graph breakpoints in the live preview — "Start Previewing Node" for a text source. Set a breakpoint on a Graph line and the preview mesh shows the value bound at that line instead of the finished material. This is the same idea as right-clicking a node in the Material Editor and choosing Start Previewing Node, except the "node" is a line of DreamShaderLang. It reuses the engine's own machinery: the generator publishes a per-source debug table mapping every (line, name) binding to the exact UMaterialExpression / output / channel-mask it produced, and a transient UPreviewMaterial — sharing the generated material's expression collection, with the probed node wired into its emissive (or MaterialAttributes / FrontMaterial for those value kinds, and a default-coordinate sample for a texture object) — is recompiled through FMaterialUpdateContext. UPreviewMaterial::ShouldCache keeps that recompile to a handful of shaders, exactly as the node-thumbnail path does. A breakpoint on a blank or comment line snaps forward to the next line that binds a value; one set before the source has ever generated is remembered and attaches on the next compile; after a recompile the probe re-resolves automatically and the client is told the line it landed on. Wire protocol: setProbe / clearProbe / probeState. See Editor Tools.

Changed

  • The streaming preview now sends raw RGBA8 frames instead of a PNG per frame. A new encoding: "raw" session reads the render target back and streams the pixels as one self-describing binary frame (a 24-byte header — size, flags, camera, resolved probe line — then the pixels), with no PNG encode on the editor side. A browser/webview client paints them straight onto a canvas. This is most of what a smooth 30–60 FPS stream costs; PNG-encoding a 512² frame per tick was tens of milliseconds of game-thread time. The legacy encoding: "png" path is unchanged for older clients.
  • Streaming got cheaper when nothing is moving. Identical frames are dropped, and after a few in a row the render clock backs off to a low idle rate until an edit, a camera/mesh change, or a probe change re-arms it (a frame rendered while shaders are still compiling never backs off). A previewControl that omits a field — including frameRate — now keeps the current value rather than resetting to 2 FPS, and it can now carry width / height / mesh so the client can drive the render size and shape without a full re-request. A force flag on previewMaterial distinguishes an explicit refresh (regenerate) from a camera/mesh re-request (reuse the last generation).

1.6.0 - 2026-08-15

Language — ten more math builtins

  • Graph gains step, smoothstep, length, cross, asin, acos, atan, atan2, reflect and refract. Every one of them had a node — or, for reflect and refract, a four-line definition — and none of them had a spelling, so the only way to write step(0.5, x) was UE.Expression(Class="Step", OutputType="float1", Y=0.5, X=x), and the failure when you wrote it the HLSL way was Unknown Graph function 'step' wrapped inside whichever call the argument belonged to. The builtin surface goes from 19 spellings to 29. See Math Builtins.
  • Three of the new names do not wire to a pin called Input, which is the whole reason the mapping is worth stating: Step and Arctangent2 name their pins Y and X, and SmoothStep names them Min / Max / Value. Argument order stays HLSL's in every case, so step(edge, x) wires argument 1 to Y and argument 2 to X and still means x >= edge.
  • length and cross join dot as the builtins with a fixed return width — 1 and 3 components, authoritative.
  • reflect and refract are the first builtins that are not one node. Unreal has no expression for either, so they are lowered to the arithmetic HLSL defines them as: four nodes for i - 2 * dot(i, n) * n, and fourteen for refraction including the If that returns zero under total internal reflection. Both are exact and both are expensive; where the surrounding code is already HLSL, a Function body still does it in one node.

This widens a reserved namespace. These ten names now shadow user code silently, the same way the original nineteen do — a Function, property or ShaderFunction named length, step or cross becomes unreachable from a Graph block with no diagnostic. Existing sources that declare one need renaming.

Not added, and not addable: matrices. The Unreal material graph has no matrix value type at all, so mul(M, v) has no Graph spelling regardless of what the DSL does — UE.Expression(Class="Transform"/"TransformPosition", …) covers space conversions and a Function HLSL body covers the rest. Still absent but reachable through UE.Expression, each having a node: exponential, logarithmic, tan, sign, round, trunc and distance. The decompiler is unchanged, so the new nodes export as generic UE.Expression(Class="…", …) calls rather than round-tripping to the builtin spelling — the same asymmetry Fmod already has. reflect and refract cannot round-trip at all, since they leave behind ordinary arithmetic nodes with nothing marking their origin.

Diagnostics — DSHnnnn codes

  • Until now the only thing identifying a DreamShader failure was its English wording. All 120 parser raise sites now carry a code instead: DSH1xxx path resolution, DSH2xxx lexer and syntax, DSH3xxx sections and declarations, DSH7xxx properties, parameters and settings. The code rides on FDreamShaderTextError alongside the FText. See Diagnostics.
  • Nothing on the wire changedFDreamShaderDiagnosticRecord::Code and the "code" field in diagnostics.json already existed.
  • The generator does not raise codes yet: 112 codes are documented, while the full catalogue of all 659 messages, grouped by stage, stays authoritative until the generator's remaining ~561 raise sites are tagged too.

Localization — the editor UI speaks your language; the wire format does not

  • Editor-facing text — the DreamShader Gen page, the settings section, slow-task progress, parser and decompiler diagnostics — moved from FString literals to LOCTEXT / NSLOCTEXT, and the diagnostic records that carry it are FText. Simplified Chinese ships with it: Content/Localization/DreamShader/zh-Hans/DreamShader.locres, loaded through the LocalizationTargets entry in DreamShader.uplugin with an Editor loading policy. FString overloads are kept alongside every converted signature. Thanks to @youli42 — PR #24.
  • diagnostics.json, diagnostics/*.json, bridge.db and the preview WebSocket stay English in every editor culture. The VSCode and Rider extensions parse those, so a translated editor must not change a byte of them. FTextInspector::GetSourceString is only half an answer: it hands back the source string for a plain LOCTEXT, but for an FText::Format result it returns the localized substituted display, so one message site adopting FText::Format would have leaked translated text onto the wire. ToInvariantWireString instead replays an FText's historic format data — the source pattern, every argument rendered in the invariant culture, nested formats recursively, number grouping off so 12345 never becomes 12,345 — and every JSON / SQLite / WebSocket write goes through it.

Plugin source roots

  • Every enabled plugin that ships a DShader folder now contributes its own source root, so a plugin can carry the .dsm / .dsf / .dsh files that build its materials instead of parking them in the project's tree. Root="Plugin.<Name>" has been able to write assets into a plugin since 1.2.0; the source half was missing. Discovery, the dependency graph and generate-all pick plugin roots up with no further configuration. See Project Layout.
  • Project Settings ▸ DreamPlugin ▸ Dream Shader ▸ Paths ▸ Scan Plugin Source Directories (bScanPluginSourceDirectories, default on) turns the plugin scan off.
  • A plugin-root file defaults to its own plugin's mount point. A .dsm under Plugins/MoonToon/DShader with no Root= attribute now generates into /MoonToon, not /Game — source and asset stay in the plugin that ships them. Only an absent or whitespace-only Root is defaulted, so Root="/" opts back into /Game.
  • Imports never cross roots. A file resolves its imports against its own root and that root's Packages folder only. Two plugins shipping the same relative path can no longer shadow one another, and disabling a plugin cannot silently change what another root's import means.
  • Root-qualified importsimport "Plugin.MoonToon:Shared/Toon.dsh"; — are the one way to cross a root deliberately. The qualifier is Project, Plugin.<Name> or Plugins.<Name> (/ spells the same as .), matched case-insensitively. The : is load-bearing: Plugin.MoonToon/Shared/Common.dsh could not be told apart from a relative path through a folder of that name. See Imports.
  • The source-directory watcher registers one watch per root, DreamShader.code-workspace lists one folders entry per root, and the DreamShader Gen page labels a plugin-root file with its root name in the row subtitle — typing a plugin's name filters to everything it ships.

Editor and generation

  • Graph layout runs on in-memory materials. Interactive compiles are memory-only, and the placement pass used to be skipped for them outright — so the graph you saw after a save was not a badly laid out graph, it was an unlaid out one: the tall single column of construction coordinates, wires strung across it. Project Settings ▸ … ▸ Compiler ▸ Lay Out In-Memory Graphs (bLayoutInMemoryGraphs, default on) turns it back off. See Project Settings.
  • Automatic layout places nodes at their real size. Every node used to be assumed 320 × 150 and spaced on a fixed 420 × 220 grid, so nothing that draws taller than 220 fitted — a TextureSample's preview thumbnail alone is 106, a Custom node grows a row per pin — and those nodes overlapped their neighbours and pushed out through the comment box meant to contain them. Sizes are now estimated from what the node widget actually assembles.
  • Long edges get lanes, and chains come out straight. An edge spanning several ranks now reserves a dummy slot in each column it crosses. Crossing reduction only ever compares neighbours one rank apart, so without lanes it could not see those edges at all — which is what let a graph read as a ball of wire even though each column, taken on its own, was tidy. Placement then runs four straightening passes, each repairing the column with an isotonic regression.
  • Blocks pack into columns instead of one stack. A material with several connected outputs used to become a ribbon tens of thousands of units tall. Blocks are now packed into columns against a height budget of max(2400, sqrt(totalArea / 1.6)).
  • The DreamShader Gen page reads diagnostics from the bridge, not by re-parsing the diagnostics.json the bridge had just written. One consequence worth knowing: those records live for the editor session, so the page no longer shows errors left over from a previous session before you compile again.

Fixed

  • The plugin builds again on UE 5.5 and 5.6. Five UE 5.7 APIs had been used without a gate, so the plugin compiled only on the engine it was written against. All five now route through DreamShaderVersionCompat.h: Materials/MaterialParameters.h (5.7 and later), UMaterialExpression::ShouldShowPreview() (5.7), UMaterialExpressionScalarParameter::ControlType and its two neighbours (5.7), UMaterialExpressionCustomOutput::GetInputValueType (5.6), and six engine expression classes — SceneDepth, SceneColor, ObjectRadius, ObjectBounds, PerInstanceRandom, PerInstanceFadeAmount — that are UCLASS() with no export macro and whose StaticClass() does not resolve from a plugin before 5.6. That last one is an LNK2019, not a compile error: it passes every compile-time check and only a full RunUAT BuildPlugin sees it. Below 5.6 the class is looked up by script path instead, so the UE.* builtins behave the same.
  • Two missing includes that unity builds had been papering over (UObject/Package.h, Engine/EngineTypes.h), which only a non-unity compile exposes.

Tools

  • The editor bridge answers now, and says whether it is alive. It had an inbound half and nothing else: a request produced no reply, no error file and no log line — a malformed one simply vanished — and a client's only way to guess whether an editor was running at all was to look for bridge.db and hope. The bridge now publishes Bridge/status.json (protocol, pid, project, plugin version, busy / busyAction, lastResult, heartbeat), rewritten every 2 s and deleted on shutdown so a missing file means "not running" definitively rather than "timed out", and answers any request carrying a requestId in Bridge/Responses/<requestId>.json with ok, durationMs, message and this compile's diagnostics. A ping action was added; bad scopes, missing sourceFile and unknown actions are now error responses instead of silent no-ops. See Editor Tools.
  • .skill/build-plugin.ps1RunUAT BuildPlugin across a list of engine roots, one PASS / FAIL line each, exit code = the number that failed. This is what caught the five ungated newer-engine APIs above; an editor build against one engine cannot, and neither can any compile-time check, because one of the five is a link error.
  • Tools/Localization/localization_lint.ps1 — checks LOCTEXT_NAMESPACE define/undef pairing, rejects it in headers, and flags literals that gather cannot see, with an I18N-EXEMPT escape for text that is deliberately not display text.

1.5.1 - 2026-08-02

Documentation and tooling only. No plugin code changed, so a project on 1.5.0 needs no migration.

Added

  • .skill/ — an agent skill set for DreamShaderLang, in the Claude Code SKILL.md format: dream-shader-create (a description becomes a .dsm that provably compiles), dream-shader-optimize (decompiler output becomes a source a human would write), dream-shader-decompile, dream-shader-verify and dream-shader-diagnose.
  • .skill/dsc.ps1 — a headless driver around -run=DreamShader. It resolves the engine from the .uproject's EngineAssociation, finds the project by walking up, de-duplicates the doubled LogInit echo of every LogDreamShader line, and classifies each asset the run wrote against git. -CleanNew then deletes exactly the untracked ones and prunes the emptied folders.
  • .skill/sync-skills.ps1 — publishes the tree into .claude/skills, rewriting the relative Docs/ links and the driver path against the destination. -Check exits 1 on drift.

Changed

  • Both READMEs restructured around the reference manual. The sections that had become abridged copies of Docs/ pages — Properties, Graph, MaterialAttributes, Substrate, Material Layers, VirtualFunction, Configuration, Release — now link to the page that owns them, which is also the page that gets maintained. 459 → 279 lines, and the two languages are kept structurally identical.

Fixed

  • The release archive now ships Shaders/, README.zh-CN.md and .skill/. Up to 1.5.0 the packaging step copied a seven-item allowlist and skipped anything missing silently, so an archive install had no Shaders/DreamShaderBuiltins.ush for the /Plugin/DreamShader virtual shader directory to resolve against.

1.5.0 - 2026-08-02

The release that unifies compilation. The two old backends collapse into one ThinCustom path, generated materials become memory-only in the editor, and a new Material Content Browser tab is where you look at them. The language picks up optional section =, Group() scopes and Slider().

Language (DreamShaderLang 1.5)

  • Section = is now optional: write Properties { … }, Settings { … } and Graph { … } without the assignment. Both spellings stay valid — see Sections.
  • Properties Group("Name") { … } scopes a group onto every parameter it contains. Groups nest and compose, so Group("Outer") { Group("Inner") { … } } yields Outer|Inner — see Metadata and Groups.
  • Slider(min, max) shorthand sets a scalar parameter's UI range. It expands to the reflected SliderMin / SliderMax properties and is the one metadata entry written without an =.
  • Asset paths can follow = directly, and bare quoted paths are accepted — see Asset References.
  • Single-output functions can be used as return values (x = Fn(…)), and Graph builtins now match the Function path: fract, mod and fmod — see Math Builtins.
  • Live preview streaming keeps the editor and language-server previews in sync while you type.
  • true / false are graph literals and materialize as StaticBool nodes, so an opt StaticBool X = false input default generates the Preview-pin node Unreal requires — it ignores PreviewValue for static-bool inputs.
  • StaticBool resolves as a one-component type at call sites.
  • Texture parameter types whose token carries no dimension — TextureObjectParameter — take their dimension from the assigned default asset, so a Texture2DArray, TextureCube or VolumeTexture default is accepted. Explicit tokens such as Texture2D and Texture2DArray still validate strictly.

Backend — one unified compilation path

  • The Graph and the experimental Instance backends are collapsed into a single ThinCustom path: DreamShaderLang compiles to a real node graph on a hidden base UMaterial, wrapped by a thin UDreamShaderMaterialInstance. The engine compiles and enumerates the material natively, so Substrate, static switches, virtual textures, MaterialAttributes and cook correctness all come from the real graph. See Backend.
  • Bit-identical SM6 render parity with the previous Graph backend, verified across Unlit, textured, and DefaultLit MaterialAttributes cases.
  • The hidden base is a subobject of the instance — one asset, one package, invisible in the Content Browser, with no separate MB_DreamThinBase_* sibling and no cross-package parent import to lose at cook. See In-memory Materials.
  • A single Default Compiler Backend project setting replaces the old In-Memory toggle. See Project Settings.

Deprecated since 1.5.0

Use ThinCustom instead.

Backend = "Instance" and DefaultBackend = Instance are retained as aliases for ThinCustom. The legacy graphless instance backend is retired; there is no runtime Instance backend left. The spelling is kept for one deprecation window so existing sources keep compiling, and it produces no diagnostic.

Editor — Material Content Browser

  • New DreamShader Material Content Browser tab under Tools ▸ DreamShader, with two pages: Project browses, filters and inspects every material and material instance under /Game, including the full inheritance chain; Dream Shader Gen lists the sources with live preview, search, filters, compile-all, and load-time error surfacing. See Editor Tools.
  • Create material instances from any material through a folder picker, and materialize in-memory (preview-only) materials to disk on demand.
  • Content Browser context-menu actions, and a toggle to show or hide DreamShader's memory-only materials.

Decompiler

  • Faithful round-trip for Substrate materials and renamed graph channels. The exporter derives channel swizzles from the write mask rather than the channel name, so recompiled materials match the source bit-for-bit. See Decompiler.

Decompiling a hand-authored material and generating it back could fail on graph shapes Unreal itself accepts. Found on LGUI's LexUI_ImageAndFont, LexUI_RectBlock and MF_LexUI_SDF_Font.

  • Switch-style nodes — StaticSwitch, FeatureLevelSwitch, QualitySwitch, ShadingPathSwitch, VertexInterpolator and friends — report no output value type, so the "assume float4" fallback oversized them and everything downstream. An AppendVector fed by a float3 material-function output was emitted as float5(...); appends are now clamped to a float4, with a warning when a count still disagrees.
  • VertexColor is emitted as float4 so the alpha pin's swizzle is valid. It used to be typed from the RGB pin and produce .a on a three-component value.
  • An input's own channel mask now replaces the connected pin's mask instead of stacking on it, so .rgb.a no longer appears when a graph wires the RGB pin but masks alpha.
  • A StaticBool function input keeps the StaticBool type token. bool declares a scalar pin, so the input used to come back as a float and reject every static-bool value passed to it.
  • Comment, #Region and description text carrying newlines or tabs is escaped, so a multi-line comment no longer splits the directive across lines.
  • A Custom node's additional outputs are declared on every emission of that node, and reading one no longer rewrites the node's own return type. Previously the emission that did not select the extra output produced a node without it, and the code body assigning to it failed at shader-compile time with use of undeclared identifier — long after generation reported success.

Fixed

  • Cook: assets are materialized on the cook director only, and a generation error now fails the cook instead of shipping a stale asset.
  • Generation refuses to overwrite assets DreamShader did not generate, and pre-validates graph syntax before clearing the target material.
  • Generated-include identity hashes the project-relative source path; stored source paths are project-relative and no longer carry a generated-at timestamp.
  • Runtime builds: guarded the editor-only UEnum::HasMetaData call so non-editor and Shipping (store) builds compile (#12).
  • Bridge: adopted FCoreDelegates::GetOnPostEngineInit for UE 5.8, and constrained Clean Generated Shaders to Intermediate with per-file deletes.

Compatibility

  • Unreal Engine 5.3 through 5.8 (Win64).

Source snapshot - 2026-06-10

Landed in the source tree between 1.4.0 and 1.5.0.

Automation baseline

  • Added a DreamShader automation-test baseline covering minimal material parsing, minimal material generation, .dsf plus import generation, Substrate material generation, source-hash skip, and a commandlet single-source compile smoke test.
  • The tests write temporary .dsm / .dsf / .dsh files under DShader/Tests/Automation, generate temporary assets under /Game/DreamShaderTests/Automation/…, and clean up after the run.
  • source hash is unchanged is the stable assertion text for an unchanged source file skipping a duplicate generation.
  • Added the explicit bridge kill switch -NoDreamShaderEditorBridge for automation runs, so bridge startup scans stay out of the test log.

1.4.0 - 2026-06-06

Compatibility

  • Added Unreal Engine 5.3 through 5.7 compatibility coverage.
  • Verified single-plugin RunUAT BuildPlugin builds for UE 5.3, 5.4, 5.5, 5.6 and 5.7 on Win64.
  • UE 5.7 remains the active development target; UE 5.3 and 5.4 may need the MSVC 14.38 toolchain on Windows.

Substrate

  • Completed the Substrate generation path: Substrate graph values, the Base.FrontMaterial output binding, and the Substrate.* wrappers. Substrate itself requires UE 5.4 or newer with Substrate enabled in the project — see Substrate Nodes.
  • The generator, the decompiler, type propagation and the editor manifest all recognize the Substrate type and its wrappers.

Material Preview

  • The editor bridge can resolve a .dsm source, generate a preview material, and write Saved/DreamShader/Bridge/preview.json plus Preview/*.png.
  • Added a local WebSocket preview service, listening on 127.0.0.1:17864 by default, which feeds results and continuous frames to the VSCode preview panel.

1.3.9 - 2026-05-29

Maintenance

  • Plugin version metadata updated to 1.3.9.
  • README, release notes and documentation references synchronized with the current language capabilities.

1.3.8 - 2026-05-25

Texture Support

  • Added VolumeTexture property parsing, code generation and default-texture handling.
  • Texture object subtypes are preserved during code generation, so Texture2D, Texture2DArray and VolumeTexture inputs reach the generated HLSL with the correct Unreal texture type.

Plugin Cleanup

  • Removed built-in shader library path support from the project settings and the documentation.

1.3.7 - 2026-05-18

Decompiler

  • Generic UE.Expression(...) decompilation exports reflected literal properties, so unsupported MaterialExpression nodes retain more editable state.
  • TextureSampleParameter2D nodes with connected graph inputs — UV coordinates, for instance — are exported as graph expressions instead of plain Properties declarations.
  • Fixed decompilation of MaterialExpressionCustom nodes with dynamic named inputs and custom output-type metadata.

Performance

  • Improved import performance for very large decompiled materials: less per-node package dirtying, throttled progress-text updates, and automatic layout skipped on large generated graphs.

1.3.6 - 2026-05-12

Build Fixes

  • DreamShaderSettings.h includes MaterialDomain.h explicitly, so projects that include the settings header directly resolve EMaterialDomain reliably.

1.3.5 - 2026-05-11

ShaderFunction Calls

  • Graph accepts statement-style multi-output ShaderFunction and VirtualFunction calls: positional inputs first, output target variables after.

Dream Shader Function Files

  • Added .dsf Dream Shader Function files for reusable generated ShaderFunction assets.
  • .dsm and .dsf files can import .dsf files, so generated functions are reusable across DreamShader sources.
  • Added .dsf source discovery, dependency tracking, and the VSCode workspace file association.

Decompiler

  • Added Content Browser export actions: UMaterial to .dsm, UMaterialFunction to .dsf.
  • Decompiled files are written under DShader/Decompiled/Materials or DShader/Decompiled/Functions with unique file names.
  • Common constants, parameters, arithmetic nodes, swizzles, texture samples, Custom nodes and MaterialFunction calls are exported as DreamShader graph text; less common reflected nodes fall back to UE.Expression(...).

1.3.4 - 2026-05-11

Output Initializers

  • Outputs accepts initialized declarations such as vec3 Color = Tint;.
  • A Shader block can use initialized output declarations with an empty Graph = {} block.

1.3.3 - 2026-05-11

Graph Swizzles

  • Fixed vector property component counts, so vec2 / vec3 properties bind through RG / RGB instead of always using RGBA.
  • Fixed non-sequential swizzles such as .gbr by generating explicit ComponentMask and AppendVector nodes.

1.3.2 - 2026-05-11

Material Function Generation

  • Plugin version metadata updated to 1.3.2.
  • Generated ShaderFunction input and output IDs are preserved across regeneration, so existing MaterialFunctionCall nodes in ordinary Unreal materials keep their connections.
  • The Graph and Custom/HLSL generation paths skip unused generated property nodes.
  • Improved generated node placement, and avoided Unreal's full automatic layout pass on DreamShader-generated material graphs.
  • Fixed a crash when regenerating an opened material function asset whose expressions were still rooted by the editor.

1.3.1 - 2026-05-09

Function Calls

  • Single-output Function and GraphFunction calls can be used as value expressions, for example Color = Texture::Sample2DRGB(BaseTex, UV0); — see Calls.
  • Multi-output Function and GraphFunction calls still require explicit out variables, for example Texture::Sample2D(BaseTex, UV0, Color, Alpha);.

Graph Functions

  • Added top-level and namespaced GraphFunction blocks for reusable HLSL Custom-node logic.
  • GraphFunction remains HLSL, but UE.* calls inside its body are converted into material nodes and passed into the Custom node as generated inputs.
  • Added GraphFunction argument validation, recursive-call detection, and explicit out-variable writeback.

1.3.0 - 2026-05-08

Shader Layer Functions

  • Added top-level ShaderLayer(Name="...", Root="...") and ShaderLayerBlend(Name="...", Root="...") blocks.
  • Generated layer assets use Unreal's native UMaterialFunctionMaterialLayer / UMaterialFunctionMaterialLayerBlend classes.
  • ShaderLayer / ShaderLayerBlend reuse the existing Properties, Inputs, Outputs, Settings and Graph sections.
  • Added validation that a Shader Layer block outputs exactly one MaterialAttributes value, and that a Shader Layer Blend block declares at least two MaterialAttributes inputs.
  • Vector parameter properties keep their RGBA output available in Graph, so .a / .w reads alpha and assignments to lower component counts use the leading channels automatically.

Deprecated since 1.3.0

Use ShaderLayer instead.

MaterialLayer and MaterialLayerBlend remain compatibility aliases and emit warnings. New source should use ShaderLayer and ShaderLayerBlend.

1.2.10 - 2026-05-08

VSCode MaterialExpression Manifest

  • Added editor-side export of reflected UMaterialExpression metadata to Saved/DreamShader/Bridge/material-expressions.json.
  • The manifest is refreshed on editor bridge startup and when the DreamShader VSCode workspace is opened.
  • Exported metadata covers expression class names, editable reflected properties, expression inputs, output pins, and inferred DreamShader OutputType hints.
  • The release workflow downloads the latest dreamshader-language-support GitHub Release assets and attaches them to DreamShader releases.

1.2.8 - 2026-05-05

Project Settings and Editor Entry Points

  • Plugin version metadata updated to 1.2.8.
  • The DreamShader.uplugin documentation link now points at https://lang.64hz.cn/, and the support link at the GitHub home page.
  • The Unreal Project Settings category moved from Plugins to DreamPlugin, with the section displayed as Dream Shader and described as Dream Shader Settings — see Project Settings.
  • Added the OpenInNewWindow setting under the Editor category. On by default, opening the DreamShader VSCode workspace from Unreal opens a new window; off, --reuse-window is appended and an existing VSCode window is reused.

1.2.7 - 2026-05-05

Unreal 5.7 Compatibility

  • Plugin version metadata updated to 1.2.7.
  • The generator picks up the header dependencies for UMaterialExpressionFunctionInput / UMaterialExpressionFunctionOutput, matching the newer Unreal material-function node build environment.
  • The Moon Engine specific MooaEncodedAttribute0 through MooaEncodedAttribute4 outputs are now wrapped in MOON_ENGINE conditional compilation, so ordinary engine builds no longer depend on those custom material attributes.
  • This release does not change .dsm / .dsh syntax; it is output targets and engine compatibility.

1.2.6 - 2026-04-30

ShaderFunction Properties

  • Added a Properties section to ShaderFunction, declaring property and helper nodes local to the generated material function.
  • Added const property declarations for scalar, vector and texture helper nodes that are not externally adjustable parameters.
  • ShaderFunction Inputs preview defaults can reference the same function's Properties, for example opt Texture2D BaseColorTex = PreviewTex;.

1.2.5 - 2026-04-30

Material Attributes

  • Added MaterialAttributes as a graph value type for Shader, ShaderFunction and VirtualFunction signatures.
  • Added struct-like member writes such as Attrs.BaseColor = Color; and Attrs.Roughness = Roughness;.
  • Added Base.MaterialAttributes = Attrs; output binding, which enables Use Material Attributes on the generated material automatically.
  • MaterialAttributes values can be returned from generated or virtual material functions and passed through Graph assignments.

1.2.4 - 2026-04-30

Parameter Reflection

  • The documented comma-style metadata suffix was replaced with a semicolon-separated trailing reflection block on declarations.
  • Parameter reflection blocks can set any reflected UMaterialExpression property exposed by the generated parameter node.
  • The basic float, vector and texture shorthand declarations use the same reflection path as explicit parameter node declarations.
  • Texture sample parameters can configure reflected properties such as SamplerType, SamplerSource, MipValueMode, AutomaticViewMipBias, ConstCoordinate and ConstMipValue.

1.2.3 - 2026-04-29

Parameters

  • Added declaration metadata [Group="...", SortPriority=32, Description="..."] for material Properties and for function input/output declarations.
  • Added explicit parameter node declarations: ScalarParameter, VectorParameter, TextureObjectParameter, the texture sample parameter family, StaticBoolParameter and StaticSwitchParameter — see Property Types.
  • Added StaticSwitchParameter graph calls, for example UseDetail(True=detailColor, False=baseColor).
  • Added UE.CollectionParam(Collection=Path(...), Parameter="...") for Material Parameter Collection reads.

Function Defaults

  • Added opt inputs for ShaderFunction and VirtualFunction.
  • Added the default call argument for optional material function inputs, preserving Unreal FunctionInput preview defaults.
  • Generated ShaderFunction assets write input and output descriptions and sort priorities to the FunctionInput / FunctionOutput nodes.
  • VirtualFunction copy, create and sync emit optional inputs, preview defaults and pin metadata when available.

1.2.2 - 2026-04-29

VirtualFunction Workflow

  • CreateVirtualFunction reuses the existing declaration for the selected material function instead of creating duplicate .dsh files.
  • When a matching declaration already exists, the material function DreamShader menu shows OpenVirtualFunction and Copy Virtual Function Reference instead of the create and copy-definition actions.
  • OpenVirtualFunction opens the existing declaration in VSCode and jumps to the declaration location where possible.
  • Added startup validation and refresh for VirtualFunction declarations under DShader, reporting missing source UMaterialFunction assets and updating changed signatures.

Import Compatibility

  • import "File.dsh" works with or without a trailing semicolon in the Unreal generator import pass.

1.2.1 - 2026-04-29

Editor Workflow

  • Replaced the single material function toolbar action with a DreamShader dropdown menu.
  • Added CopyVirtualFunction, CreateVirtualFunction and CopyVirtualFunctionCall to the material function editor toolbar and the material function asset context menu.
  • CreateVirtualFunction writes a .dsh declaration under the configured DShader/VirtualFunctions directory and opens it in the default external editor.
  • CopyVirtualFunctionCall copies a ready-to-paste Graph call using the generated input names and the first output.
  • Added Open Dream Shader Workspace (VSCode) to the editor Tools menu and the DreamShader toolbar section. It writes DShader/DreamShader.code-workspace, opens it in VSCode when available, and falls back to the default editor or Notepad.

Release

  • Added a GitHub Actions release workflow that packages the plugin source and publishes a GitHub Release from version tags or manual workflow dispatch.

1.2.0 - 2026-04-28

VirtualFunction

  • Added VirtualFunction(Name="...") declarations for existing Unreal UMaterialFunction assets.
  • VirtualFunction calls work from Graph like ShaderFunction calls, without generating or overwriting the referenced asset.
  • Options.Asset supports Path(Game, "..."), Path(Engine, "..."), Path(Plugin.PluginName, "...") / Path(Plugins.PluginName, "..."), and full Unreal object paths.
  • Added material function context-menu and Material Editor toolbar actions that copy a complete VirtualFunction declaration with inputs, outputs and options.

Asset Roots

  • Root="Plugin.PluginName" maps to the project plugin content root, physically saving generated assets under [Project]/Plugins/PluginName/Content.
  • Plugins.PluginName and Plugins/PluginName remain compatibility spellings.

Tooling

  • The VSCode extension picked up VirtualFunction highlighting, completion, hover, snippets, signature help and diagnostics.
  • Project content plugin names are completed inside Path(Plugins.) as well.

1.1.4 - 2026-04-28

Asset Generation

  • Root="Plugins.PluginName" / Root="Plugins/PluginName" are supported as compatibility spellings, resolving identically to Root="Plugin.PluginName".
  • This avoids Plugins.MoonToon being treated as an ordinary /Game/Plugins_MoonToon subdirectory — see Asset Paths.

1.1.3 - 2026-04-28

Asset Generation

  • Clarified that Root="Plugin.PluginName" targets an enabled project content plugin only.
  • Plugin.MoonToon resolves to the UE package root /MoonToon, physically saved under [Project]/Plugins/MoonToon/Content.
  • The generator validates that the target plugin sits in the project Plugins directory, is enabled, can contain content, and has a Content directory.

1.1.2 - 2026-04-28

Language and Generation

  • Added the Root top-level attribute to Shader and ShaderFunction, selecting the generated asset's root path.
  • Root defaults to Game and also accepts Plugin.PluginName, saving the generated UMaterial or UMaterialFunction into an enabled project content plugin root.
  • When a ShaderFunction is called from Graph, the generated UMaterialFunction asset is resolved and loaded through that function's own Root.

Documentation

  • Updated the asset generation notes in the plugin README, the language reference and the site.

1.1.1 - 2026-04-26

Fixed

  • Fixed a Function call with several out parameters in Graph, where the Custom node's trailing output pins received no value.
  • The first out of a multi-output Function still comes back through the Custom node's main return; the second and later out parameters now generate explicit assignments writing the __ds_*_outN temporaries back to the matching Additional Output, for example Output_ToonA = __ds_xxx_out1;.

Compatibility

  • A patch release. No .dsm / .dsh changes are required.

1.1.0 - 2026-04-22

Language and Graph

  • Renamed the Code = { ... } block in materials and material functions to Graph = { ... }, separating the graph DSL from the HLSL helper layer.
  • Added basic if / else support to Graph — see Control Flow.
  • Improved output expression handling: an output binding records source text rather than only a variable name, allowing more flexible parsing of graph outputs.
  • Improved type conversion and generic UE.* MaterialExpression argument handling, reducing friction when patching nodes in by hand.

Function and Generation

  • Added Function SelfContained and Function Inline, embedding helper dependencies in the Custom node so generated materials can be handed to projects without the DreamShader plugin installed.
  • An ordinary Function still generates a .ush helper include, referenced from the Custom node.
  • Added a cleanup workflow for generated shader files, reducing leftover historical includes.

Packages and Workflow

  • Added the Package system, distributing and installing reusable .dsh shared libraries through GitHub — see Packages.
  • Added the import dependency graph: saving a shared .dsh finds the affected .dsm files and refreshes them incrementally. Later versions extend this to .dsf dependencies.
  • Cached the config directory, cutting the cost of repeatedly parsing project settings.

Diagnostics and Compatibility

  • Diagnostics carry stage, platform and quality context, separating parse, generation and material compile problems — see Diagnostics.
  • Removed the default mapping for the Toon shading model, so generated results do not unexpectedly depend on a project-custom shading model.
  • Updated the README, language reference and example documentation to match.

1.0.0 - 2026-04-21

Initial Version

  • The .dsm / .dsh file model: Unreal materials and shared helpers described as text sources.
  • Shader(Name="...") generates a UMaterial; ShaderFunction(Name="...") generates a UMaterialFunction.
  • Properties, Settings, Outputs and the early graph-building syntax.
  • HLSL-style Function helpers, generating Custom node code.
  • Baseline documentation and examples, as the first public DreamShaderLang release.

On this page

1.8.0 - 2026-08-21Divergence — three ways outA rebuild is atomicA rebuild is refused while the asset is open in an asset editorThe build key replaces the plain source hashA batch of source files compiles in dependency orderOne editor owns the bridge for a projectAn asset that exists on disk is rebuilt on diskFixedAdded1.7.1 - 2026-08-16Fixed1.7.0 - 2026-08-16AddedChanged1.6.0 - 2026-08-15Language — ten more math builtinsDiagnostics — DSHnnnn codesLocalization — the editor UI speaks your language; the wire format does notPlugin source rootsEditor and generationFixedTools1.5.1 - 2026-08-02AddedChangedFixed1.5.0 - 2026-08-02Language (DreamShaderLang 1.5)Backend — one unified compilation pathEditor — Material Content BrowserDecompilerFixedCompatibilitySource snapshot - 2026-06-10Automation baseline1.4.0 - 2026-06-06CompatibilitySubstrateMaterial Preview1.3.9 - 2026-05-29Maintenance1.3.8 - 2026-05-25Texture SupportPlugin Cleanup1.3.7 - 2026-05-18DecompilerPerformance1.3.6 - 2026-05-12Build Fixes1.3.5 - 2026-05-11ShaderFunction CallsDream Shader Function FilesDecompiler1.3.4 - 2026-05-11Output Initializers1.3.3 - 2026-05-11Graph Swizzles1.3.2 - 2026-05-11Material Function Generation1.3.1 - 2026-05-09Function CallsGraph Functions1.3.0 - 2026-05-08Shader Layer Functions1.2.10 - 2026-05-08VSCode MaterialExpression Manifest1.2.8 - 2026-05-05Project Settings and Editor Entry Points1.2.7 - 2026-05-05Unreal 5.7 Compatibility1.2.6 - 2026-04-30ShaderFunction Properties1.2.5 - 2026-04-30Material Attributes1.2.4 - 2026-04-30Parameter Reflection1.2.3 - 2026-04-29ParametersFunction Defaults1.2.2 - 2026-04-29VirtualFunction WorkflowImport Compatibility1.2.1 - 2026-04-29Editor WorkflowRelease1.2.0 - 2026-04-28VirtualFunctionAsset RootsTooling1.1.4 - 2026-04-28Asset Generation1.1.3 - 2026-04-28Asset Generation1.1.2 - 2026-04-28Language and GenerationDocumentation1.1.1 - 2026-04-26FixedCompatibility1.1.0 - 2026-04-22Language and GraphFunction and GenerationPackages and WorkflowDiagnostics and Compatibility1.0.0 - 2026-04-21Initial Version