Search Docs
Loading...
Skip to content

Changelog

Explore the recent changes made to CreativeEditor SDK.

Our release notes contain more details on features and changes.

Download the full changelog as a plain text file.

Recent Releases

Version v1.82.3 Changelog October 18, 2026

Breaking Changes

Non Breaking Changes

  • Engine/Android: The canvas now shows the page and its content on devices that run OpenGL ES through ANGLE on Qualcomm GPUs, such as Googlebooks. Before, parts of the page could stay empty in the editor, while exports were correct.
  • Engine/Android: The engine logs its graphics setup to logcat at debug level, under the tag UBIQU. The log names the GPU driver, the drawing surface format and size, and Display P3 support. It also reports when the engine cannot create, re-create or present its drawing surface.
  • Engine/Android: A TextureView now shows the canvas without block noise on Googlebooks and other devices with Qualcomm Adreno X1 GPUs. Before, only a SurfaceView showed the canvas correctly there.

Version v1.84.0 Changelog October 15, 2026

Breaking Changes

Single Page Mode

  • Engine: Single page mode is now part of the engine. Turn it on with the setting features/singlePageModeEnabled. Use scene.setCurrentPages to select one page, or two facing pages, to show. Use scene.getCurrentPages to read them. All other pages stay in the scene and in saved files. An export of the scene holds every page, also the pages that the mode does not show.
  • Engine: block.isVisible now returns only the value that you set with block.setVisible. A page that single page mode does not show returns true. Use scene.getCurrentPages to find the shown pages.
  • Engine: If single page mode is on when a scene loads, the mode controls the pages that the scene saved as hidden. The next save stores these pages as visible.
  • Editor/Web: The layers panel lists only the pages that the mode shows, and lists every page again when the mode is off. A row of the list no longer changes the pages in view.
  • Editor/Web: Use cesdk.switchPage to show a page and scroll to it. It replaces unstable_switchPage, which is deprecated. The single page mode toggle now changes features/singlePageModeEnabled.

Themes & Contrast

  • Editor/Web: Added the light-high-contrast and dark-high-contrast themes. They meet WCAG 2.1 AAA text contrast and draw every control boundary at 3:1 or more. The label of an active control meets AA while it is hovered or pressed. Select them with cesdk.ui.setTheme() or in the settings panel.
  • Editor/Web: cesdk.ui.getTheme() can now return 'light-high-contrast' or 'dark-high-contrast'. The 'system' theme resolves to them when the operating system asks for more contrast.
    • A check like getTheme() === 'dark' does not match the dark high-contrast theme. Use getTheme().startsWith('dark') instead.
    • TypeScript code that stores the result as 'light' | 'dark' must use the Theme type.
    • Custom themes scoped with [data-ubq-theme='light'] or [data-ubq-theme='dark'] do not apply to the high-contrast themes. Add those selectors only if your colors keep the contrast the high-contrast themes provide.
  • Editor/Web: Refreshed the light and dark themes. Buttons, inputs, tab bars and button groups have a 1px edge, active controls show a tint of the active color, and each theme uses one accent color whose button labels meet WCAG AA. Accent and danger split buttons separate their parts with a small gap.
  • Editor/Web: Added the --ubq-interactive-stroke, --ubq-interactive-accent-stroke, --ubq-interactive-danger-stroke and --ubq-interactive-active-wash theme tokens. A custom theme that sets --ubq-interactive-accent-default or --ubq-interactive-danger-default should also set the matching stroke token. Set --ubq-interactive-stroke to transparent to remove the edges of neutral controls.
  • Editor/Web: Color inputs now show hover and pressed feedback.
  • Editor/Web: The placeholder over a broken block now uses a fixed light red in every theme, so its icon stays visible. Its color comes from the new --ubq-static-error-state token instead of --ubq-notice-error.

PDF Conversion Runtime

  • Plugins/Web: The print-ready PDF plugin runs Ghostscript in a worker and supports sharing the conversion runtime with EPS import. Existing PDF/X options and convertToPDFX3 remain available. For npm integrations, host worker.browser.js, gs.js, and gs.wasm from @imgly/pdf-conversion-utils and inject a runtime with that assetBaseURL. For direct imports of dist/index.mjs, deploy the complete plugin dist/ tree, including pdf-conversion/. Update CSP to permit workers and WASM compilation; cross-origin workers also require blob: and the asset origin. Hosting the worker on the application origin avoids the blob requirement. See the plugin README for migration details.

Structured Errors

  • Engine/Web, Engine/Node: engine.block.export, exportWithColorMask, exportAudio, saveToArchive, getVideoFillThumbnail, getVideoFillThumbnailAtlas, getPageThumbnailAtlas, generateThumbnailAtTimeOffset and engine.scene.saveToArchive now reject with an EngineError instead of a string. On Web, engine.block.exportVideo does the same. engine.block.getDominantColors, generateVideoThumbnailSequence, generateAudioThumbnailSequence and engine.editor.getBufferData report an EngineError instead of a plain Error. Read error.message and branch on error.code instead of treating the reason as a string.
  • Engine/Node Native: engine.block.saveToString and engine.block.saveToArchive reject an unknown block with an error that has a code, as @cesdk/node does. Before, the error had no code.

Video Capabilities

  • Engine/Web: The engine now implements video.decode.checkSupport and video.encode.checkSupport, also in @cesdk/node and @cesdk/node-native, so an integration without the editor can ask whether this browser plays or exports video. Each action measures the codecs on first use, then answers. CE.SDK overrides both to add its dialogs, so the editor behaves as before.
  • Editor/Web: video.decode.checkSupport now resolves to its answer asynchronously, like video.encode.checkSupport. cesdk.actions.run() callers are unaffected. Code that calls the function from cesdk.actions.get('video.decode.checkSupport') directly, or uses the VideoDecodeCheckSupportAction type, must now await it.
  • Editor/Web: Deprecated cesdk.utils.supportsVideoDecode() and cesdk.utils.supportsVideoEncode(). Run the video.decode.checkSupport or video.encode.checkSupport action with { dialog: false } instead. The actions also answer false on iOS and iPadOS. supportsVideoDecode() again only checks that the video APIs exist, as before v1.80, so it can answer true in a browser that cannot decode H.264. supportsVideoEncode() measures the codecs on its first call, so that call can take a moment.
  • Engine/Web: CreativeEngine.init() no longer measures the codecs. They are measured when something asks: the two video actions, or engine.editor.checkCapabilities(). Until then, engine.editor.getCapabilityState() reports each codec as 'unchecked'. Init still measures the storage capabilities, which export staging and archiving read. The engine answers each capability once, so the first call can take a moment and later calls return at once. A measurement the browser cuts off, as a background tab can, is no answer: the engine measures again rather than report 'unsupported'.
  • Engine/Node: Deprecated engine.editor.isCapabilitySupported(). Use engine.editor.getCapabilityState() instead. CreativeEngine.init() no longer measures the codecs in @cesdk/node. Node has no WebCodecs, so the answer was always no.
  • Editor/Web: CE.SDK’s video.decode.checkSupport and video.encode.checkSupport wrap the engine’s checks, which answer for the codecs alone, and add the iOS and iPadOS rule and the dialogs.

Actions

  • Editor/Web: cesdk.actions.run() is now typed to resolve to the value an asynchronous action resolves to. Before, the type said it resolved to a promise of that value. Code that awaited the result was already correct.
  • Engine/Web: engine.actions.get(id) now returns every registered action, including the ones the engine registers such as history.undo, which it returns as an async function. Before, it returned undefined for them. The function keeps running that action after the id is registered over, so an override can build on it. Also in @cesdk/node and @cesdk/node-native.
  • Engine/Web: engine.actions.unregister(id) now removes the action whoever registered it, and nothing comes back in its place. Before, unregistering an id the engine registers restored the engine’s action. To restore it, register the function engine.actions.get(id) returned before you replaced it. Unregistering an id the engine runs on input, such as select, turns that interaction off. Also in @cesdk/node and @cesdk/node-native.

Animation & Cropping

  • Engine: A Ken Burns animation now has room to pan when you attach it through the API, also in headless renders. Attaching it replaces the crop of the block. To use your own crop, set it after the image or video has loaded. Older scenes get the room when they load, with the crop centered.

Android List Builders

  • Editor/Android: addFirst with an alignment in the modify block of a list builder now adds each item at the front of its group, as addFirst without an alignment does. Before, each further call added its item after the previous one. If you call addFirst more than once with the same alignment, reverse the order of those calls to keep your current button order.

Deprecations

All items below remain functional this release; migrate before they are removed.

Video Capability APIs

  • Engine/Web: Deprecated supportsVideo(), checkVideoSupport(), supportsVideoExport() and checkVideoExportSupport(). The first two only check that the video APIs exist, and the other two run a codec test of their own that can disagree with the engine. Run the video.decode.checkSupport or video.encode.checkSupport action instead.
  • Engine/Web: Deprecated engine.editor.isCapabilitySupported(). It answers false for a capability that is not measured yet, the same as for one the platform lacks. Use engine.editor.getCapabilityState(), which tells the two apart, or run a video action to ask about video. The deprecated method logs a warning the first time it answers false for that reason.

New Features

Agent Skills

  • Editor/Flutter, Editor/RN: The imgly-sdk agent skills plugin now covers Flutter and React Native. The docs, explain, and build skills detect a Flutter or React Native project, read the bundled imgly_editor, imgly_camera, @imgly/editor-react-native, and @imgly/camera-react-native API digests, guides, and examples, and point at the bundled Swift and Android references for the customizations that live in native code. To change part of a prebuilt editor, such as its dock, they start from the wrapper’s own Swift and Kotlin sources, which the plugin now bundles. Install it with claude plugin install imgly-sdk@imgly, codex plugin add imgly-sdk@imgly, or npx skills add imgly/agent-skills; an existing cesdk@imgly install picks up the new references on its next update.

Examples & Starter Kits

  • Editor/Android: Make the curated guides and starter kits available in the runnable public examples app.
  • Editor/iOS: Added a Content Moderation starter kit that checks a design’s images and text against a content moderation service, reports flagged categories grouped by block in a validation sheet, and gates export on the result.

Video Capabilities

  • Engine/Web: Added engine.editor.getCapabilityState(capability), also in @cesdk/node and @cesdk/node-native. It answers 'unchecked' until something asks for the capability to be measured, 'pending' while it is measured, then 'supported' or 'unsupported'. Only the codecs on the web are ever 'unchecked' or 'pending'. In CE.SDK, a component that renders from it updates by itself when the state changes.

Shapes & Asset Sources

  • Editor/Web + Engine/Web + Engine/Node: The Outline, Gradient and Image groups of the Shapes library now include a Line, as the Filled group already did. The bundled ly.img.vector.shape source lists the same new Lines.

Node Native Live View

  • Engine/Node Native: New experimental live view. attachCanvas() hands the update loop to a host. The host reads each frame as RGBA with straight alpha and forwards mouse, wheel and keyboard input to the engine.
  • Engine/Node Native: New experimental TerminalCanvas shows the engine in a terminal that supports the kitty graphics protocol, such as kitty, Ghostty or WezTerm.

Notifications

  • Editor/Web: Users can choose how long notifications stay on screen (5, 10 or 20 seconds, or never) from the notifications themselves. The default is 5 seconds, and the choice persists in the browser.

Placeholders

  • Editor/Web: A new ly.img.placeholder.inspectorBar button marks and opens placeholder settings, and the Placeholder tab gained an “Always highlight on canvas” toggle, off by default.

UI Customization

  • Editor/Web: builder.Button accepts activeStateStyle, as builder.Dropdown already does. Set it to 'pill' to mark an active button with an underline instead of an outline.
  • Editor/Web: A ly.img.assetLibrary.dock entry takes an optional tooltip, as a string or a function that returns one.

Separate Audio

  • Editor/Web: The clip menu of a video clip in the timeline has a new “Separate Audio” item below “Trim”. It moves the audio of the clip into new audio clips, one per audio track, and mutes the video clip. The audio clips start with the clip, keep its speed and volume, and take its name. The item shows for background clips and overlay clips whose video has audio.
  • Editor/Web: The item is the ly.img.video.clip.menu.separateAudio component of the clip menu. Leave it out of the component order to hide it.

Text Formatting & Tabs

  • Engine: Added subscript and superscript text formatting. Use setTextScriptStyle and getTextScriptStyles to move a range of text above or below the baseline. Lines and exports grow to include the shifted text. Use the block properties text/superscriptFontScale, text/superscriptVerticalShift, text/subscriptFontScale and text/subscriptVerticalShift to change the size and the offset of the shifted text.
  • Engine: Text blocks now lay out tab characters. A tab moves the text after it to the next tab stop and wraps to the next line at the frame edge. The tabStopInterval setting sets the distance between stops (default 2 × the base font size). The Tab key inserts a tab character through the new text.tab action. In a list, Tab and Shift+Tab change the indent level instead, through text.tab and text.tabBack. A justified line that holds a tab keeps its natural width.

Print Marks & Grid Controls

  • Editor/Web: A color picker in the Safety Margin controls sets the color of the safety margin on every page, as it does for the grid and the bleed. Enable or disable it with the new ly.img.page.printMarks.safetyMargin.color feature key.
  • Editor/Web: A lock next to Grid Width and Grid Height links the two values. While it is on, a change to one sets both.

Improvements & Behavior Changes

Agent Skills

  • Engine/Web, Editor/Web, Engine/Swift, Editor/iOS, Engine/Android, Editor/Android: The CE.SDK agent skills now ship as one imgly-sdk plugin with three skills, docs, explain, and build, that cover Web, Swift, and Android. Each skill detects the platform from the project and loads the matching bundled references. Install it with claude plugin install imgly-sdk@imgly, codex plugin add imgly-sdk@imgly, or npx skills add imgly/agent-skills. The per-framework docs-<framework> skills and the separate cesdk-swift and cesdk-android plugins are retired; an existing cesdk@imgly install picks up the new layout on its next update.
  • Engine/Web, Editor/Web, Engine/Swift, Editor/iOS, Engine/Android, Editor/Android, Editor/Flutter, Editor/RN: The CE.SDK agent plugin is now listed as “IMG.LY SDK” and installs as imgly-sdk@imgly: claude plugin install imgly-sdk@imgly or codex plugin add imgly-sdk@imgly. In Claude Code, an existing cesdk@imgly install keeps updating; its skills are now /imgly-sdk:docs, /imgly-sdk:explain, and /imgly-sdk:build. In Codex, replace it: codex plugin remove cesdk@imgly, then codex plugin add imgly-sdk@imgly. Agents now pick the plugin for any request to build a photo, video or design editor, even when CE.SDK is not named. Prerelease channels have their own marketplace names, so imgly-sdk@imgly-next and imgly-sdk@imgly-dev can be added beside stable; keep only one channel enabled.
  • Engine/Web, Editor/Web, Engine/Swift, Editor/iOS, Engine/Android, Editor/Android, Editor/Flutter, Editor/RN: The imgly-sdk agent skills plugin now reads the CE.SDK guides for the project’s CE.SDK version from https://img.ly/docs/cesdk/ as Markdown instead of bundling them, so the skills need network access for guides. The API digests and agent rules stay bundled. The build skill now clones the public starter kit repositories at the branch of the plugin’s CE.SDK version instead of copying bundled kits.

Camera Orientation

  • Camera/Android + Camera/iOS: Camera controls, dialogs, photo previews, and captured photos and videos now follow the device orientation. On Android, photos and videos use the orientation at capture time, and capture results include matching dimensions and positions. Videos keep their starting orientation during recording. Reaction recordings keep the first take’s orientation until all takes are deleted. Front-camera photos are mirrored to match the preview.

Inspector & Page List

  • Editor/Web: The fill button in the inspector bar shows a thumbnail of the block’s image or video, in place of the fill type icon. The inspector and the fill panel show the same thumbnail.
  • Editor/Web: The opacity button in the inspector bar shows the opacity of the selected block, for example “50%”, in place of the “Opacity” label. Screen readers read the button as “Opacity: 50%”, with the input.opacity label.
  • Editor/Web: The page list shows each page number before the page name, for example “Page 3 - Cover”. The number follows the page order, so a moved page shows its new number.
  • Editor/Web: The Shadow button moved from the inspector bar into the Style popover, below Blur, in the CE.SDK editors and starter kits. When only one Style control applies to the selected block, the inspector bar shows that control directly. A shape or a text block, for example, keeps its Shadow button on the bar.
  • Editor/Web: The rotation field in the inspector now shows decimal degrees with up to two decimals, such as 12.5°, instead of rounding the displayed angle to whole degrees.
  • Editor/Web: The inspector now saves a new block name when you click outside the name field. Before, only Enter saved it.

Notifications

  • Editor/Web: Every notification now stays for that chosen time, so the duration values short, medium, long and custom milliseconds no longer change it. duration: 'infinite', errors and notifications with an action stay until closed.
  • Editor/Web: Notifications show the time they have left, and keyboard focus pauses them like mouse hover does.

Accessibility & Interaction

  • Engine: A placeholder block now gets a dotted selection and hover frame, so color is not its only marker. The alwaysHighlightPlaceholders setting draws its frame dotted too, at half opacity.
  • Editor/Web: Scrollable panels, menus, lists, the dock and the inspector bar now fade out at each edge that has more content. The fade is always on.
  • Editor/Web: While text is edited on the canvas, screen readers announce “Text content” in the language of the editor.
  • Editor/Web: Controls have clearer accessible names for screen reader and speech recognition users.
  • Editor/Android: Screen readers announce which blend mode and which font is selected in their lists.
  • Editor/Web: Controls that show only an icon now have a tooltip and an accessible name, also on narrow screens.
  • Editor/Web: Disabled number fields, and a disabled builder.Checkbox with a truncated label, show their tooltip on hover.

Themes & Canvas Colors

  • Editor/Web: The canvas colors (page title, selection frame, placeholder highlight, progress, broken-block fill) now follow every theme change, including cesdk.ui.setTheme() and a change of the operating system’s color scheme or contrast. A theme change overwrites values set with engine.editor.setSetting(); to keep custom canvas colors, set the matching theme tokens in CSS instead.

Keyboard Shortcuts

  • Engine/Web: The engine now binds the keyboard shortcut listener to the canvas element it creates, so the default shortcuts reach the engine without extra wiring. Pass your own canvas in the configuration and the engine has no element of its own, so name the listening element with engine.shortcuts.setRoot.

API Types & Validation

  • Editor/Web: The payload option of builder.Component() is now typed as ComponentPayload instead of any.
  • Engine: setInt, setBool, setFloat and their setSetting… forms store a number in an enum only when an option has that number. setString and setSettingString return an error for an enum. setSettingBool, setSettingInt and setSettingFloat on an enum setting are deprecated. Use setEnum or setSettingEnum for an enum.

Localization & Labels

  • Editor/Web: The stroke toggle is now named “Enable Stroke”, and the fill toggle of a text “Enable Fill”, instead of “Enable Color”.
  • Editor/Web: Error dialogs for start-up, export, templates and uploads show a specific message in English and German, for example “Lizenz abgelaufen”. An error without such a message shows the dialog’s general text, never the English engine text.
  • Editor/Web: A transition that fails to apply shows a translated notification instead of the English engine text.
  • Editor/Web: In German, text presets, text combinations and caption presets show German names.
  • Editor/Web: German sticker and shape names are corrected, for example “Klebeband 01” instead of “Tape01”.
  • Editor/Web: The Tilt Shift effect is named “Tilt Shift” in English and “Tilt-Shift” in German, instead of “Tiltshift”.
  • Engine/Web, Engine/Node: Every asset in the default asset sources has a German label.
  • Engine/Web, Engine/Node: The ly.img.effect.extrude_blur asset has the English label “Extrude Blur” instead of “Explode”, as in the editor.
  • Engine/Web, Engine/Node: The English labels “Thumps up” and “instagram Profile Photo” now read “Thumbs up” and “Instagram Profile Photo”.
  • Editor/Web: German texts use one term for each concept, for example “Inspektor”, “Arbeitsfläche” and “Schleife”.
  • Editor/Web: With the differentPageFormats feature flag, the Resize panel now reads “Resize Selected Pages” with the page count when several pages are selected. Before, it read “Resize Page”. The new translation key is component.pageResizePanel.labelSelectedPages.

Sliders & Page Resizing

  • Editor/iOS, Editor/Android: The opacity slider in the Layer sheet now snaps to 5% steps, so the value shown is the value set. This makes it possible to set the same opacity on several blocks.
  • Editor/iOS, Editor/Android: Percent readouts on sliders now round instead of truncate, so 89.5% shows 90 and not 89.
  • Editor/iOS: The star Points and polygon Sides sliders in the Shape sheet now snap to whole numbers and show the value without decimals.
  • Editor/Web: The Resize Page panel shows “Mixed” for a width or height that differs between the pages it resizes. Apply then changes only the values you type, and each page keeps its own value for the other side.

PDF Conversion Limits

  • Plugins/Web: Print-ready PDF conversion accepts outputs larger than 100 MiB by default. Use maxOutputBytes to set a limit when needed.

Resource Error Display

  • Engine: A block whose file failed now draws its content below the error color. A block that you put in an error state with setState still covers its content.

Scene Persistence

  • Engine/Web, Engine/Node: block.saveToString also allows bundle resources by default, so a page with text in a default font saves with the default schemes.
  • Engine: Scenes and archives no longer store which blocks are selected, and loading one no longer selects blocks. Undo and redo still restore the selection.

Text Decoration

  • Engine: A text stroke now also outlines the underline, strikethrough, and overline. Before, only the letters got the outline.

Video Resource Lifetime

  • Engine: The engine releases the memory of a video when no block uses it. On Android, a removed audio or video fill also releases its decoder.

Starter Kit Timelines

  • Editor/Web: The Video Animations and Export using CE.SDK Renderer starter kits, and the Adopter mode of the Video Placeholders starter kit, now use the simplified video timeline of the Video Editor starter kit, so the canvas keeps its room on laptop screens. Creator mode keeps the full timeline.

Performance & Package Size

  • Engine: An image embedded as a data: URI no longer leaves two extra copies of itself in memory for as long as the app runs. A scene with 20 embedded images of 4 MB now keeps 84 MB less after it is closed, and it loads faster, because the engine reads such a URI once instead of three times.
  • Engine: A loop of block.export() calls uses much less memory on a scene with many blocks. Before, a large scene filled the WebAssembly heap and the next engine call stopped with Aborted().
  • Engine: Large scenes load faster and use less memory. A 300-page scene loads in about two thirds of the time, with 84 MB less peak memory. LUT filters and word-by-word text animations do less work on each frame.
  • Engine: Reduced the bundled CMYK profile size from 584 KB to 296 KB without changing color conversion.
  • Engine: You can now add and change text quickly in scenes with many blocks. Before, each text change took more time as the scene grew.

Android Diagnostics

  • Engine/Android: The engine logs its graphics setup to logcat at debug level, under the tag UBIQU. The log names the GPU driver, the drawing surface format and size, and Display P3 support. It also reports when the engine cannot create, re-create or present its drawing surface.

Bug Fixes

Engine

  • In the Adopter role, a single drag on a group that allows selecting and moving now selects and moves it. Before, the first drag only selected the group.
  • The engine no longer crashes on an old scene whose fill type does not match its data.
  • An animation asset with an unknown type, or on a slot or block that does not support it, returns an error. Before, the engine crashed. A Ken Burns animation from the animation asset source now stays on the block.
  • Audio exports now play each clip and fade at its time on the timeline, the same on every export.
  • Audio extracted with createAudioFromVideo or createAudiosFromVideo and keepTrimSettings now plays like the video. It plays the same part of the video at the same speed, lasts as long as the clip, and keeps the volume, mute state, looping and audio fades of the video. The audio of a video faster than 3x is force muted, like the video. Before, the audio of a faster or slower clip played the wrong part at normal speed.
  • Exporting the audio of a block, a track or a group no longer crashes the engine or removes blocks from the page.
  • An audio export that fails no longer changes what the next audio export contains.
  • combine, createCutoutFromOperation and createCutoutFromBlocks return an error that names the block when they cannot compute the boolean operation. Before, the result left out that block and the call reported success.
  • Crop mode now stays open while a transition covers the selected clip, and while the playhead sits outside it. Before, the mode dropped back to Transform on the next change.
  • createCutoutFromBlocks now makes a cutout from a text block in a headless engine, such as @cesdk/node. Before, the call crashed the engine unless useExistingShapeInformation was false.
  • The engine no longer crashes on a damaged GIF or MP4 file.
  • Fixed blocks jumping to a wrong size or position when a drag runs while the camera is still moving, for example right after a pinch or a zoom.
  • exportVideo on a block that is not attached to the scene now fails with BLOCK.NOT_ATTACHED_TO_SCENE, as export does. The native engines crashed the process instead.
  • exportWithColorMask with the mime type application/pdf now reports “Color masking is not supported for PDF export.” A PDF export never runs the raster pass that builds the mask, so the call used to report a non-specific “Export failed.” The hint of the same error for SVG no longer points at PDF.
  • A block no longer jumps at the start of a move. The text cursor stays correct while an input method writes accented or non-Latin text.
  • Fixed the thin bands that crossed a large gradient fill. A wide, low-contrast gradient now stays smooth on the canvas and in every export.
  • Activating a new history with setActiveHistory keeps the current scene, including edits not yet committed with addUndoStep. Before, those edits were lost.
  • Pages that overlap in a free layout now show the page in front. Before, the shared area was rendered blank during preview.
  • The bleed band and the bleed line now cover the whole bleed of a page with page/marginScale above 1 that is not square.
  • On any page with page/marginScale above 1, the page background now fills the bleed and the page bounding box includes it. Blocks snap to the bleed edge and can move up to it, and a click on the bleed selects the page.
  • PDF export preserves fractional page dimensions instead of rounding them to whole points, keeping the physical page size and bleed geometry accurate.
  • A PDF export with exportPdfWithUnderlayer keeps its underlayer when underlayerOffset is negative. Some pages lost the white spot separation at particular offset values, for example -0.2 and -1.0, while nearby values worked
  • Two engines that play audio at the same time no longer mix their audio.
  • The playhead now keeps its position when a split puts a clip into a new track, or when a clip moves into a track. Before, it jumped back to 5 seconds or to the old end of the track.
  • A block that failed to load its image or video now shows the new file when you replace it. Before, the block kept the first error and never asked for the new file.
  • A block waits, and no longer shows an error, while it loads the file that replaced a file that failed.
  • When you resize or crop a block and it moves to another page, the edge you do not drag now stays in place. Before, the block jumped by the distance between the pages.
  • A resource load that finishes after its block is gone no longer kills the process. With a URI resolver installed, forceLoadResources could end the process at the next scene change or at shutdown.
  • saveToString for blocks now checks the resources of the whole block hierarchy, the typeface fonts of text blocks included. Saving a page reports the resources of its children to the persistence callback and writes the persisted URLs. A child resource on a scheme that the call does not allow now gives an error, as documented.
  • Loading a page from an archive made with saveToArchive now links the images and other resources of its children to the files in the archive. Before, only the page itself was linked, and its children showed missing images.
  • A save no longer changes the resource URLs of the scene while uploads are in progress, or after one of them fails.
  • After a save, a block that already used the URL an upload returned keeps that URL.
  • Loading blocks from a string that is not valid returns an error. Before, it returned no blocks, or it changed blocks in the scene.
  • A scene load with features/removeForegroundTracksOnSceneLoad no longer loses clips.
  • Undo, redo, paste and duplicate no longer turn the appearance/animation scope back on for blocks of older scenes.
  • A scene that you build through the API now reports the size of its first page in scene/pageDimensions until you set a value. The first save of such a scene now matches every later save.
  • A load no longer replaces the scene/pageDimensions that you set with the size of the first page.
  • Applying a template now keeps the page size of a scene that you just built. On a scene without pages, the template keeps its own page size.
  • Text with an Outer dashed or dotted stroke and no fill no longer draws a thin line along the letters. The line appeared when the dash start and end caps had different shapes.
  • A strikethrough, underline or overline now draws in front of the text stroke. The outline of the letters covered it before.
  • A text block without a typeface now accepts the font weight and style that the engine reports for it. Before, setTextFontWeight and setTextFontStyle rejected the value that getTextRuns or getTextFontWeights had just returned.
  • getTextRuns now reports the real weight and typeface of a text block that has no typeface, instead of normal and an empty typeface.
  • The font weight, font style, bold and italic APIs now use the typeface of the font that a text block renders with when the block has no typeface.
  • A change of the scene design unit keeps the text line breaks, the visible lines and the automatic font size. Before, a text block with a tight frame could break into two lines after setDesignUnit('Pixel').
  • Fixed text typed over a selection that took the font size of the next line. The new text now gets the style of the first selected character.
  • A triple-click now selects a line without its line break, so typing over the line keeps it apart from the next line.
  • The drop shadow of curved text, and of text with a per-letter, per-word or per-line animation, now follows the opacity of the text, its stroke and its decorations. Before, this text cast an opaque shadow, unlike other text.
  • getEnum, getInt, getGlobalScope and the other typed getters return an error for an unknown value, or for a property or setting of another type. Before, the engine could crash or return a wrong value.
  • The engine no longer crashes on an unknown enum value in a scene.
  • getSettingEnum returns an error for an unknown value, or for a setting of another type. Before, the engine could crash.
  • A video export that fails no longer blocks the next one. Before, every later video export on the same engine failed with a concurrent encoding error.
  • A video export fails with an error when the video encoder stops, for example on a frame that is too large for it. Before, the export never ended, and on Linux with the open-source codecs the process crashed.
  • Fixed pages that stayed a grey placeholder when the view jumped to them with viewport culling enabled. This happened after zoomToBlock on load followed by a page thumbnail export, and after a page above them was resized.
  • A zoom with the mouse wheel or with a pinch keeps the design point under the pointer in place, also in a scene with many pages. Before, the view moved away from that point.
  • setZoomLevel keeps the content at the center of the view in place, also in a scene with many pages.
  • A style change on one caption now also updates the size and line breaks of the other captions on the track.
  • An absolute base path with [, # or a space in a folder name now finds every bundled file, the ICU data included. A base path that is neither a file path nor a URI no longer stops the engine; the bundled files then fail to load.
  • Fixed split on a clip with an outgoing transition. Only the second half keeps the transition, so the cut between the halves has none.
  • Only the Creator role sees the dotted selection and hover frame of a placeholder block. All other roles see a solid frame again, as before 1.84. The alwaysHighlightPlaceholders frame stays dotted for every role.
  • Fixed a crash when a page with a track or a group was added below the visible part of a scene, for example after a video export.

Editor/Web

  • In the Adopter role, the fill type picker, the Replace button and the shadow and stroke panels are locked when the block’s scopes deny those changes. Before, an Adopter could still change them.
  • In the Adopter role, the color fill button is hidden when the block allows only cropping (layer/crop), because a color cannot be cropped.
  • In the Adopter role, the advanced text options open when the block allows text styling (text/character). Their frame sizing buttons also need layer/resize. Before, the options needed both scopes to open.
  • The cesdk agent skills plugin converts a starter kit to JavaScript again. Before, the transpile script could not load TypeScript, and npm run build failed with tsc: command not found after the conversion. The script now also converts .tsx files.
  • For a release candidate or nightly version, the cesdk agent skills plugin now tells the agent how to install the starter kits. Before, npm install failed with ERESOLVE for kits that use an importer.
  • The code blocks in the cesdk agent skills plugin now render as code. Before, they showed as literal backslashes and backticks.
  • The web starter kits in the cesdk agent skills plugin now install with npm install and start with npm run dev. Before, npm install failed with Unsupported URL Type "workspace:". The kits now match the public starter kit repositories.
  • The automatic font-size range now shows the same unit as the font size input. On a Pixel scene it labelled pixel values as pt.
  • Switching the font size to Auto no longer shrinks the text on a Pixel scene when the ly.img.scene.fontSizeUnit feature is disabled. The range was written in points and stored as pixels.
  • A builder.Dropdown with more items than fit its height now scrolls.
  • The Advanced row in the caption inspector now fits the inspector panel and shortens its label with an ellipsis, as in the text inspector.
  • Fixed the Placeholder item in the video clip menu showing for caption clips when a custom order includes it. Captions have no Placeholder view, so choosing it did nothing; the item is now hidden for them.
  • The “Set as clip” item in the video clip menu now shows only for overlay clips. Before, a custom order could add it to the background clip menu, where it resized the clip to fill the page and moved it to the end of the track.
  • The “Set as overlay” item in the video clip menu now shows only for background track clips. Added to a caption menu through a custom order, it moved the caption off its caption track onto the page.
  • Moving the color picker’s saturation and brightness field with the arrow keys now adds its own undo step.
  • With ly.img.crop.panel.autoOpen disabled, closing the crop panel no longer ends crop mode, and a page’s crop handles stay visible while in crop mode.
  • Fixed shortcuts that took the arrow, Home, End, Page Up, Page Down, Backspace and Delete keys from a focused field in their own panel. In the crop panel, the arrow keys now step the size, scale and offset fields and move the straighten slider.
  • Loading a scene while the crop panel is open no longer fails with an error.
  • An open asset library shows its labels in the new language after cesdk.i18n.setLocale().
  • Enter Group and Select Group in the canvas menu keep their icon when the menu hides labels.
  • When an upload fails, the progress dialog closes and the error shows. Before, a failed image upload left the progress dialog open.
  • In German, the toggles of fills, strokes, shadows, filters and effects name their property again, for example “Desert aktivieren”.
  • In German, the preset tooltip shows the preset name instead of $(input.preset).
  • In German, effect names, the Credits dialog, the asset credits and the photo editor dock are no longer English.
  • The Settings panel shows the names of the “End Cap” and “Dash Pattern” features instead of raw keys.
  • The Stack, Line Wipe and Clock Wipe transitions show their names instead of raw keys.
  • The unsupported-browser alert is German when the editor configuration sets locale: 'de'.
  • cesdk.utils.showLoadingDialog labels its cancel button “Cancel” when you pass no label.
  • An ly.img.assetLibrary.dock entry with a key and no label uses the libraries.<key>.label translation. Before, it showed libraries.ly.img.assetLibrary.dock.label.
  • Fixed long video clips showing a broken image instead of thumbnails in the video timeline, for example a two-hour clip at the default timeline scale.
  • Fixed the on-screen keyboard not being reserved on mobile when the editor runs inside an iframe, which left the edited text behind the keyboard. The keyboard is now measured against the embedding page when that page is same-origin.
  • Entering Preview mode deselects the selected block when the Adopter role cannot select it.
  • Closing a notification twice no longer closes a different notification. This happened when users double-clicked the close button, or when cesdk.ui.dismissNotification() ran twice with the same ID. A double-click also no longer calls onDismiss twice.
  • Fixed number fields switching the decimal separator on focus, for example from 24.5 to 24,5.
  • Fixed number fields that waited for Enter after an arrow key pressed with a modifier. Shift+ArrowUp and Shift+ArrowDown now change the value by ten steps, and the arrow keys with Cmd, Ctrl or Alt change it by one step. Both apply the value at once, like the plain arrow keys. The stepper buttons of a field without min now also step from the shown value, like the arrow keys.
  • Number fields now show the stored value once you confirm an edit, also when the editor adjusts or refuses the typed number. For example, polygon sides round 5.5 to 6 instead of showing 5.5 and logging an error.
  • The flip orientation button in the Page panel keeps its icon on square pages, where it is disabled, instead of looking missing.
  • In the Photo Editor, the Adjust, Filter and Effects dock buttons are now disabled when the page has a color or gradient fill instead of an image. Hovering them shows “Requires an image”. Before, a click did nothing.
  • Pressing Escape right after clicking the inspector’s rename button now closes the name field. Before, it could close only the button’s tooltip, and a second Escape was needed.
  • The Replace panel now closes when the selected block has nothing left to replace, for example after its fill changes from an image to a color. Before, it showed an empty “Replace Shape” panel.
  • The Resize Page panel now shows the current size of the pages it resizes. Before, it could show an old size after a resize through the API or from the page inspector.
  • Fixed a Block <id> is unknown error when a scene loads while a scroll.toPage or scroll.toBlock animation is running. The animation now stops when a different scene loads, and the action resolves.
  • Reset in the settings panel restores every setting the panel changes to the value it had when the panel was first opened. The Reset button is disabled while nothing differs.
  • Trimming or moving a clip in the video timeline that shows only the selected track no longer moves that clip below the other layers. The clips the timeline hides keep their layer order.
  • A single click on a color, volume, crop straighten or asset property slider now adds an undo step. Tabbing onto a slider no longer adds an empty one.
  • Fixed a gap in the video timeline after you drop a clip next to a clip with a transition. The dropped clip now joins the transition. A clip that you drag away from a transition now stays where you drop it.
  • Turning off ly.img.transform.rotation hides only the rotation input, and turning off ly.img.transform.flip hides only the flip buttons. Before, turning off either one hid both.
  • Fixed an error when the selected video or audio block is destroyed through the API.
  • The “Placeholder” item in a video timeline clip’s menu now opens the Placeholder tab of the inspector. Before, it only marked the clip as a placeholder and the inspector stayed on the Design tab.
  • The video timeline playhead no longer covers the top of clips when the time ruler is turned off, and its line reaches the bottom of the timeline. The timeline now fits its tracks exactly when a horizontal scrollbar appears or its controls wrap onto a second row.
  • A collapsed video timeline now stays collapsed until the user expands it. Adding or removing clips no longer opens it again, and the same holds after timeline.collapse.
  • The video timeline now fits its tracks exactly. It no longer scrolls up and down by a few pixels or shows a vertical scrollbar when all tracks fit.
  • In the AI Editor starter kit’s photo mode, the AI Edit dock button is now disabled when the page has no image (for example a color or gradient fill), and hovering it shows “Requires an image”, like Adjust, Filter and Effects. Before, it stayed enabled and opened a panel whose Generate button could never be used.
  • Dragging a finger across the saturation/brightness field of the color picker now changes the color on touch devices.
  • The Style menu opens for a block whose duotone filter has custom colors. Before, the editor showed “Unknown Error” when the colors matched none of the built-in duotone filters.
  • Fixed a console error loop that could freeze the tab when the page removed the editor container after CreativeEditorSDK.create() failed. The editor’s error screen now closes itself once its container leaves the page.
  • Dialogs and popovers now keep focus inside their own editor when several editors run on the same page.
  • Fixed CreativeEditorSDK.create() leaving the engine running when it failed after the engine had started.
  • Safari on iPhone and iPad no longer zooms into the page when a text field gets focus. On touch devices, text fields use 16px text in the modern and normal scales. A custom theme can target touch devices with the data-ubq-touch attribute.
  • The speed controls and the Custom stroke style option show German labels in the German editor. They showed English labels before.
  • Fixed number and text fields, such as the hex color field, that showed an old value after Cmd+Z or Ctrl+Z while the design kept the new one. Undo in a focused field now discards a value that was not confirmed yet.
  • Fixed number fields, including font size, bleed, crop size and crop position, that recorded no undo step when a change brought the value back to what it was before an undo.
  • In the AI Editor (photo mode), Start With Image and Force Crop starter kits, the Adjust, Filter and Effects dock buttons (Adjust and Filter in Force Crop) are now disabled when the page has a color or gradient fill instead of an image, and hovering them shows “Requires an image”, as in the Photo Editor. Before, they stayed enabled although there was nothing for them to edit.
  • Fixed popovers that ran past the edge of a short window, which hid their last controls, for example the RGB fields of the grid color picker. A popover is now as tall as the space next to its button allows, and its content scrolls.
  • The Video Editor and Advanced Video Editor starter kits now show only video templates in the Templates panel. Before, the panel also listed the static design templates.

Editor/Android

  • Fixed a crash when the selected block is deselected or deleted while the canvas menu has an enter or exit transition.
  • The canvas menu’s bring forward and send backward buttons now update when a block is added to or removed from the selected block’s parent.
  • Fixed the timeline’s add clip and add audio menus showing an empty entry for a hidden option. A button with one visible option now triggers it directly, as its label says.
  • The icon of the fill and stroke button in the inspector bar now follows the fill color and the gradient colors of the selected block. Before, the icon kept the previous color when the fill was changed through the engine API, for example from a custom color picker.
  • The replace button of the inspector bar is now hidden for an audio block when the fill/change scope is denied.

Engine/Node

  • Audio exports in @cesdk/node are no longer silent.
  • When asset.addSource rejects a source, the Node.js process can exit after dispose().

Engine/Web

  • Fixed video export being reported as unavailable in a browser that supports it, when the page was busy while the check ran. The same fix stops PDF export and archiving from falling back to building their files in memory in that situation.
  • Fixed ReferenceError: __filename is not defined at engine start in apps built with some bundlers. This happened when a bundler replaced typeof __filename but did not define __filename, for example webpack with node.__filename: 'eval-only'. You can remove a __filename workaround that you added for @cesdk/engine. In Node.js, use @cesdk/node.
  • block.exportWithColorMask now accepts every option block.export accepts, so allowTextOverhang, exportPdfWithDeviceCMYK, pdfImageQuality and pdfChunkSize no longer report “Invalid arguments”. This applies with and without featureFlags.exportWorker.
  • block.export now accepts every export option in both call forms. With the mime type as a separate argument it refused exportPdfWithDeviceCMYK, exportPdfWithCropMarks, exportPdfWithRegistrationMarks, printMarkOffset, cropMarkLength, printMarkWidth, pdfImageQuality and pdfChunkSize. With featureFlags.exportWorker on, it also refused pdfChunkSize in the other form.
  • With featureFlags.exportWorker on, underlayerRenderRatio, underlayerMaxError, printMarkOffset, cropMarkLength and printMarkWidth now refuse NaN and Infinity, as they already did without it and as their documentation states.
  • The engine releases a held mouse button when the editor loses the pointer. Before, the back and forward buttons could crash the engine.
  • Fixed a crash (“memory access out of bounds”) that could stop the editor while several videos stream at the same time.

Engine/Web + Engine/Node Native

  • block.saveToString and scene.saveToString now reject when onDisallowedResourceScheme throws, rejects, or returns no string. Before, the call never finished and the error was an unhandled promise rejection.

Editor/iOS

  • Fixed the video editor in landscape. The timeline now collapses while a sheet is open, so sheets no longer float over a band of bare timeline chrome, and it shows one track row by default in landscape, so the canvas stays visible. The show/hide timeline toggle is now also available in landscape. Timeline customization follows: the default height is .fixed(tracks: 1) in a compact vertical size class, and Timeline.Buttons.toggleExpanded is visible in every size class by default.
  • Fixed the voiceover recording layout in landscape. The timeline lifts above the record bar, so the recording placeholder stays visible while recording.
  • Sheet detents are now distinct in landscape. Small and medium sheets no longer collapse to the same height, and the drag indicator shows when a sheet is resizable.
  • Fixed the preview-mode page layout when the editor is launched directly in landscape.
  • Sheets are taller in landscape, so their content fits: effect and filter strips no longer cut their labels, and sheets such as Add Captions and Resize no longer overlap their title.

Editor/Android, Editor/iOS

  • Fixed the previous page and next page navigation bar buttons, which stayed visible on the first and last page when the page carousel was enabled.
  • Fixed clips that overlapped after a drag and drop in a track with transitions. A transition at a cut that the drop changes is now removed, and a message tells the user.
  • Fixed the split position of a clip with an incoming transition. The clip is now split at the playhead.

Plugins/Web

  • Fixed PDF/X export creating an empty TrimBox when the input PDF has no print bounds. The print-ready PDF plugin now uses each page’s CropBox or full page bounds and preserves existing TrimBox, ArtBox, and BleedBox values.

Engine/Android

  • When addSource rejects a source because its id is in use, the registered source keeps working.

Renderer

  • Emoji now render in color. Before, they rendered as empty boxes or in black and white. The assetless images now ship the Noto Color Emoji font.
  • An engine option given on the command line, such as --default-emoji-font-file-uri or --max-image-size, now overrides the value that the scene stores.
  • Fixed video exports that stalled or showed frames in the wrong order for H.264 videos without B-frames when decoded by the AV-Licensed codec pack.

Renderer + Engine/Node

  • Fixed video exports that stopped progressing when the decoder skipped frames of a source video. On Engine/Node: Fixed video exports on Linux and Windows that stopped progressing when the decoder skipped frames of a source video.

Engine/Node Native

  • process.exit() no longer crashes while the engine downloads a font.
  • CreativeEngine.init on Windows no longer stops the Node.js process. The engine loads its bundled files, such as the ICU data, from the assets folder of the package.

Engine/Web, Engine/Node

  • features/textEditModeTransformHandlesEnabled is now part of the public Settings type, so setSetting accepts it without a TypeScript error. It keeps the resize, scale and rotate handles of a text block visible while its text is edited. Off by default.
  • createAudioFromVideo and createAudiosFromVideo now extract the audio of a video file that is not completely in memory, such as an uploaded or a remote video. Before, the audio of such a video played noise or no sound, or the call failed. The audio now arrives after the call returns. Until then, unstable_isAVResourceLoaded returns false, forceLoadAVResource waits for the audio, and the audio block is pending once it is in the scene.

Engine/Swift

  • A video export no longer crashes the app when it finishes, or after an engine was destroyed during an export.

Version v1.83.0 Changelog October 1, 2026

Breaking Changes

CMYK & Spot Colors

  • Engine: Improved CMYK colors with ICC-accurate conversion through the document CMYK profile instead of basic channel inversion. A key-only black and a rich black now render as different colors, as they print. This changes previews and raster exports. An export that keeps device CMYK is unchanged, because it writes ink values and converts nothing.

  • Engine: Improved tinted CMYK colors by scaling print ink values instead of blending the converted color toward white.

  • Engine: Fixed a spot color used as a gradient color stop, which now renders with its defined approximation. It previously rendered as magenta, the placeholder for an undefined spot color.

  • Engine: Fixed the tint of a spot color used as a drop shadow color, which rendering ignored.

  • Engine: Added the scene/colorConversionMode property, which selects how a scene converts CMYK values for display. A new scene is Managed and converts through the document CMYK profile. A scene saved before this release loads as Legacy and keeps the previous CMYK channel-inversion conversion. Set the property to Managed to adopt the accurate CMYK conversion for an existing design. Spot colors use the corrected approximation and tint handling in both modes; a CMYK spot approximation follows the selected mode.

  • Engine: convertColorToColorSpace now converts a CMYK color to sRGB through the document CMYK profile in a Managed scene, so it returns the same color the engine renders. The returned values differ from the previous channel inversion. A Legacy scene keeps the previous conversion.

  • Engine: convertColorToColorSpace now fails with COLOR.PROFILE_NOT_LOADED when a conversion needs the CMYK profile and the profile is still loading. The call requests the profile; run update() until loading finishes before converting again. If no usable profile can be loaded, conversion reports a missing profile with the failed URI. Fix the profile setting before retrying. It previously returned an unmanaged color that a later call would not repeat. A Legacy scene needs no profile and never fails this way.

  • Engine: Added loadCMYKProfile, which waits for the document CMYK profile before trying fallbackCMYKProfileUri when needed. Await it before dependent conversions and after changing profiles. CMYK fills, text, strokes, shadows, and effects keep an export waiting while the profile loads. If no usable profile can be read, the export completes with the unmanaged conversion and reports the failed URI once.

  • Engine: In Managed scenes, sRGB colors and RGB-only spot colors now convert to CMYK through the document or fallback profile. Conversion can fail while the profile loads. Wait for loadCMYKProfile() to complete before converting a color to CMYK. Profiles that only convert CMYK to RGB continue to use a simple formula.

Asset Placement, Drag & Drop

  • Engine (Android / Swift / Web / Node): Applying an asset now takes an optional placement. It puts the new block in a given parent, centered on a given point. Android: Added overloads of applyAssetSourceAsset and defaultApplyAsset that take an AssetPlacement. Swift: Added apply(sourceID:assetResult:placement:) and defaultApplyAsset(assetResult:placement:), which take an AssetPlacement. Web / Node: engine.asset.apply and engine.asset.defaultApplyAsset now take a placement option.

  • Engine: Added a drag gesture that swaps two image fills. It is off by default. The dragToSwapFills/enabled setting turns it on.

  • Engine/Web: The engine now handles drag & drop from outside the canvas. It outlines the block that would take the drop and runs the asset.drop action on release. By default the action replaces that block’s content, or adds the asset where it was released. Register your own asset.drop to change it.

  • Engine: The page/highlightDropTarget outline now follows the dragged element without a frame of delay, and marks the page the element lands on when page/reparentBlocksToSceneWhenOutOfPage is on.

  • Engine/Web: While the swap gesture holds an image, a preview of the image follows the pointer. Escape cancels the swap.

  • Editor/Web: Users can now drag a library asset onto an image to replace it. A drop on a page adds the asset where it was released. A press and hold starts the same drag on a touch screen, and on a small screen the library closes to show the canvas. The ly.img.dragAndDrop.asset feature key turns it on, and the Design, Photo, Video, Advanced and Advanced Video editors have it on.

  • Editor/Web: A configuration that lists its feature keys and predates this release does not list ly.img.dragAndDrop.asset, so its library cards can no longer be dragged. Add the key, or declare the release the configuration was written for with setEditorCompatibilityVersion.

  • Editor/Web: A library drop no longer applies the asset to the selected block. To keep that behavior, register your own asset.drop action that applies the asset to the selection.

  • Editor/Web: A drop runs the asset.drop action. Register your own with cesdk.actions.register('asset.drop', …) to handle drops from your own drag source.

  • Editor/Web: A library drag no longer carries the //ly.img.asset data type. A custom drop target that read it no longer receives library drags. It now carries the asset source and the asset id under the types ASSET_SOURCE_TYPE and ASSET_ID_TYPE, and the content kind under contentKindType(kind), which @cesdk/engine exports.

  • Editor/Web: Users can now swap two images. Press and hold an image, or hold Alt, then drag it onto another image. The dragToSwapFills/enabled engine setting turns it on, and the Design, Photo, Video, Advanced and Advanced Video editors have it on.

  • Editor/Web: Screen readers now announce the result of a drop.

Text Alignment

  • Engine: Text blocks and captions have a new alignment, Justify. It widens the gaps between words to fill the block width. The last line of a paragraph keeps its natural width. An older engine opens a scene with justified text and shows it aligned like Auto.

  • Engine (Swift + Android): Swift: HorizontalTextAlignment has a new justify case. The raw value of auto changes from 3 to 4. This fixes a crash when an app read the alignment of a text block set to Auto. Update a switch that lists every case. Android: HorizontalAlignment has a new Justify value. Update a when that lists every value.

Timeline Customization

  • Editor/iOS: Added timeline customization. Pass a Timeline.Configuration to Timeline to configure the timeline instead of rebuilding it.

  • Editor/iOS + Android: iOS: Timeline now renders its tracks alone: no header, and no “Add Clip” or “Add Audio” button. An integration that mounts Timeline — or the deprecated DefaultTimelineComponent — without a configuration loses those three surfaces and keeps compiling, so add them back with header(_:), addClip and addAudio. An integration built on the video starter kit is unchanged, because the starter kit declares all three. Android: The timeline now renders its tracks alone by default: no header, and no “Add Clip” or “Add Audio” button. Add each surface through headerListBuilder, addClipButton and addAudioButton. The video starter kit declares all three, so its timeline is unchanged.

  • Editor/iOS: Every built-in timeline item and add-content option now takes an isEnabled, matching the canvas menu buttons. DefaultTimelineComponent is deprecated and renamed to Timeline, which is now both the component and the namespace for everything that customizes it.

  • Editor/iOS: The timeline’s “Add Clip” and “Add Audio” buttons are now configurable. Choose which sources appear and in which order, relabel or re-icon them, replace what a built-in source does, hide one conditionally, or add your own with Timeline.AddClipOption.custom(…). Add Clip offers the camera and the asset library by default, and Timeline.AddClipOption.photoRoll() adds the privacy-friendly photo picker. Add Audio offers music and voiceover. Both buttons also take a title and an icon that replace the button’s own label.

  • Editor/iOS: The timeline header is now configurable. Set your own items leading, centered, or trailing with header(_:) and Timeline.ItemGroup, or adjust the built-in header with modifyHeader(_:) so later built-in items still reach your integration. addFirst(placement:_:) and addLast(placement:_:) place an item at an edge without naming a neighbouring one. Timeline.Labels.timecode, Timeline.Buttons.playPause, Timeline.Buttons.loop, Timeline.Buttons.toggleExpanded and Timeline.Spacer build the built-in items. playPause and loop take an icon, timecode and toggleExpanded a whole label, and each button also takes an action that replaces what it does.

  • Editor/iOS: The timeline height is now expressed in track rows instead of points. It grows to at most three tracks by default, or you can set a fixed number of tracks.

  • Editor/iOS: The timeline’s expanded state can now be driven by your own binding through Timeline(context:configuration:isExpanded:).

Resource Loading

  • Engine: forceLoadResources now waits for fonts. Before, it could finish while a font was still loading. Text was then measured with a different font.

  • Engine: getState now reports Pending after setFont, while the font loads. Before, it reported Ready too early.

  • Engine: forceLoadResources now reports an error when a resource did not load. Before, it reported success.

  • Migration guide: Migrating to v1.83 (iOS, Android) shows how to declare the timeline’s header and buttons.


Deprecations

All items below remain functional this release; migrate before they are removed.

Asset Sources

  • Engine (Web + Node): asset.canManageAssets is deprecated. It reports whether a source declared the deprecated canManageAssets flag, not whether assets can be added to or removed from that source. To control whether an upload button is offered, use the canAdd option on an asset library entry instead.

Stroke Caps & Scene Duration

  • Engine: setStrokeCap and getStrokeCap are now deprecated in the C++ API, as they already were on Web, Android and iOS. Use setStrokeStartCap and setStrokeEndCap to set each end, and getStrokeStartCap and getStrokeEndCap to read them.

  • Engine: getTotalSceneDuration is now deprecated in the C++ API, as it already was on Web and Android. Use getDuration with a page block instead.

Navigation Bar Configuration

  • Editor/Web: The ui.elements.navigation.position configuration option is deprecated. Use cesdk.ui.setNavigationBarPosition() instead.

New Features

Editor Configuration & Platform Support

  • Editor/Android: Add support for Android 17.

  • Editor/Web: @cesdk/cesdk-js now exports the SceneDesignUnit type.

  • Editor/Web: cesdk.setEditorCompatibilityVersion('1.82.0') pins an editor configuration to the CE.SDK version it was written for, so listed behavior changes from later versions do not apply to it. Call it after cesdk.resetEditor(), which clears any previous pin. The CE.SDK editor configurations and starter kits pin it for you; a configuration that does not pin gets the current release’s behavior.

Layers, Pages & Grouping

  • Editor/Web: Added keyboard shortcuts for the stacking order. Cmd/Ctrl+] and Cmd/Ctrl+[ move the selection one step towards the front and the back; ] and [ move it all the way. They run the new selection.bringForward, selection.sendBackward, selection.bringToFront and selection.sendToBack actions, which move every selected block and can be run directly with cesdk.actions.run('selection.bringToFront').

  • Editor/Web: Added a layer and page list, opened from a new button at the bottom of the dock. It shows the document’s pages above the layers of the page being worked on. A row selects its block, renames it in place, hides it, locks it, and opens a menu of the commands that apply to it. Holding Alt on the eye or the padlock leaves only that row shown or unlocked, and holding it again brings the rest back. Dragging a row reorders the block or moves it into a group or another page. Pressing the space under the rows or a section heading clears the selection. A selection made on the canvas scrolls its row into view. The design and advanced editor configurations declare the dock button as an ly.img.assetLibrary.dock entry keyed ly.img.layerList whose click handler opens the panel; an integration with its own dock order adds the same entry, or opens the list by its panel id, //ly.img.panel/layers.

  • Editor/Web: Every row of the layer and page list can show a thumbnail of its block, kept current as the block changes. Enabled with the ly.img.layerList.thumbnails feature.

  • Editor/Web: The list is gated by the ly.img.layerList feature and its children ly.img.layerList.panel, .visibility, .lock, .reorder, .rename and .menu, so the panel and each row control can be turned off on its own. All are enabled by default.

  • Editor/Web: ly.img.layerList.pages and ly.img.layerList.layers choose which lists the panel holds, and are both enabled by default. ly.img.layerList.combined replaces both with a single tree of every page and its layers, and is off by default.

  • Editor/Web: ly.img.layerList.canvasFollow scrolls the canvas to the block a row picks, and follows a page row only by default. cesdk.feature.set('ly.img.layerList.canvasFollow', true) follows every row; cesdk.feature.disable('ly.img.layerList.canvasFollow') follows none.

  • Editor/Web: The menu on a layer or page row is driven by the Component Order APIs under the new ly.img.layerList.menu UI area (cesdk.ui.setComponentOrder, getComponentOrder, insertOrderComponent, updateOrderComponent, removeOrderComponent). A page row and a layer row read their own order, selected with when: { rowKind: 'page' | 'layer' }. The built-in entries are registered as components (ly.img.layerList.menu.rename, .duplicate, .delete, .group, .ungroup, .bringToFront, .bringForward, .sendBackward, .sendToBack, .page.addAbove, .page.addBelow) and custom entries can be added with cesdk.ui.registerComponent.

  • Editor/Web: A row’s padlock writes the engine’s own transform lock, so it is saved with the scene and applies on the canvas. A locked block, and a block inside a locked group, does not reorder by drag, by the row menu or by the bracket keys. It can still be selected, inspected and restyled.

  • Engine: A block that already belongs to a group can be grouped again, so a group can be made inside another group from a selection. A selection that spans two containers now moves every member into the new group. isGroupable refuses a block grouped together with a container it sits inside, which would make that container a child of itself, and it also refuses a scene, a page and an empty selection.

  • Editor/Web: Added the page.add action. Override it to choose where a new page is inserted.

  • Editor/Web: Added the page.moveUp and page.moveDown actions, which move the selected page.

Snapping & Image Source Sets

  • Engine: Added equal-distance snapping. A dragged block snaps to equal spacing with its siblings, and a badge shows the size of each gap. While the snap holds the block, the engine does not round its position to the pixel grid. It is off by default. Turn it on with the features/equalDistanceSnappingEnabled setting.

  • Engine: Set features/automaticSourceSetsEnabled to true and an image fill you add gets the smaller entries of its source set by itself, so the engine can draw a small image where the full one is not needed. A fill that comes from a loaded scene gets none, and a duplicate or an undo step keeps the entries it has. The smallest entry is 512 pixels on the long edge and the largest is your own image, so an image under 1024 pixels gets none and an export still uses the full resolution. An entry is a JPEG at quality 90 for an image without alpha and a PNG for one with it. The setting is off by default. The entries are transient resources that state the buffer scheme: saveToArchive carries their bytes and needs nothing, while saveToString needs a persistence callback (on web and iOS) or the entries stored by hand first (on Android), or it reports the scheme and stops.

Print Setup, Exclusion Areas & Photobooks

  • Engine: A new exclusionArea block marks a region of a page that content must stay out of, for example a window on an envelope or a spine on a book cover. An exclusion area is left out of an export, or cut out of it with the new exclusionArea/punchOut property. The new exclusionArea/constrains property says whether the exclusion area holds content out of itself while the user drags, nudges or resizes a block. It is off by default, so a new exclusion area marks a region and nothing more. Turn it on for a region the engine must hold content out of, an envelope window or a die cut. An exclusion area groups only with another exclusion area, so the marker cannot be welded to the artwork it marks. A scene that holds an exclusion area does not open in an earlier version of the engine. See Exclusion Areas.

  • Engine: The new findAllInExclusionAreas API returns every block that overlaps an exclusion area, see Exclusion Areas.

  • Engine: Pages carry a safety inset, an inward distance from the page edge that marks where content must stay for print. Set it with the new page/safetyEnabled and page/safetyInset/left, /top, /right, /bottom properties. Its guide appears while a drag, or a move or resize with the arrow keys, comes near it. Set page/safetyRevealDuringTransform to false to draw the guide all the time. See Pages.

  • Engine: The new export options exportPdfWithCropMarks and exportPdfWithRegistrationMarks add crop marks and registration marks to a PDF export, both off by default. See the Export to PDF guide.

  • Editor/Web: Selecting an exclusion area now shows an Exclusion Area Settings panel with two toggles: whether the area holds other blocks out, and whether it cuts its region out of the export. Enable it with the new ly.img.page.printMarks.exclusionArea feature key.

  • Editor/Web: Added a Photobook Editor starter kit that builds a print-ready photo book from a template: a layouts library per page format, auto-fill that matches each photo to the slot that crops it least, live design validation, a page-spread preview, and export to PDF/X-4 with a FOGRA39 output intent. Export runs in the browser by default and can offload rendering to the bundled Node server.

  • Editor/Web: Added a Print Setup panel in the document inspector. Bleed is now set per side, with a lock that links the sides, replacing the single document-wide value.

  • Editor/Web: The Print Setup panel adds crop and registration marks to PDF exports, with a configurable offset, length and weight. The offset and length are in the scene’s design unit, the weight in points. Exports via the export button or cesdk.utils.export() include them; each print-mark option a caller passes overrides the panel for that one option. cesdk.export() and engine.block.export() are unchanged.

  • Editor/Web: Added Feature API keys 'ly.img.page.printMarks.crop' and 'ly.img.page.printMarks.registration', which control the printer’s marks in the Print Setup panel. Enabled in the advanced editor configuration, disabled elsewhere. Their parent key 'ly.img.page.printMarks' turns both on at once.

  • Editor/Web: Added Feature API key 'ly.img.page.bleedColor', which controls the bleed color row in the Print Setup panel.

  • Editor/Web: New Safety Margin controls set an inward inset on each of the four sides, seeded at 10mm when first enabled. The document inspector sets the margin for every page, and selecting a page sets that page alone. Enable the controls with the new ly.img.page.printMarks.safetyMargin feature key, which is on in the design and advanced editor configurations.

CMYK Profiles & Imported JPEGs

  • Engine: Added setCMYKProfile and setCMYKProfileFromData to use a document’s CMYK profile for previews and raster exports. You can also inspect or remove the profile and configure rendering intent and black point compensation.

  • Engine: Imported JPEG fills can retain and render their source color space, ICC profile and decode ranges without changing the original image bytes.

  • Engine: Such a definition applies only while its image buffer holds the bytes it was read from. A write that changes those bytes switches the definition off, and writing them back switches it on again.

Text Backgrounds

  • Engine: Added setTextBackgroundColor and getTextBackgroundColors. They set and read the background color of a range of text. A fully transparent color removes the background. The run background is independent of the block background and draws on top of it.

  • Engine: Added the text block properties text/backgroundPadding/left, text/backgroundPadding/top, text/backgroundPadding/right, text/backgroundPadding/bottom and text/backgroundCornerRadius. They set the size and the corners of the background of the text runs. Caption blocks get the same properties under caption/, and a caption track keeps them in sync.

Timeline, Navigation & Crop Customization

  • Editor/iOS: ForceCropMode.ifNeeded now takes a configurable threshold that sets the tolerated difference between the current dimensions and the preset, matching Android. .ifNeeded keeps the previous behavior with a default threshold of 0.0001. To tolerate a range of aspect ratios, use the midpoint of the range as the preset ratio and half of the range as the threshold.

  • Editor/Android: Added timeline customization. Configure the timeline with Timeline.remember and pass it as the bottomPanel instead of rebuilding it.

  • Editor/Android: The timeline’s “Add Clip” and “Add Audio” buttons are now configurable through the new addClipButton and addAudioButton properties of Timeline. Both buttons and their menu entries are ordinary editor components, so they take the same vectorIcon, textString, enabled and visible properties as a dock button. Use Timeline.Button.rememberAddClip and Timeline.Button.rememberAddAudio to choose and reorder the entries built by Timeline.AddClipOption.rememberCamera, rememberGallery, rememberLibrary, Timeline.AddAudioOption.rememberMusic and rememberVoiceover, or add your own source with Timeline.AddClipOption.remember and Timeline.AddAudioOption.remember. Add Clip offers the camera and the asset library by default, Add Audio offers music and voiceover. Assign null to remove a button, or provide your own EditorComponent to replace it. A button with one remaining entry triggers it directly instead of opening a menu, and names that entry instead of showing the generic label.

  • Editor/Android: The timeline header is now configurable through the new headerListBuilder property of Timeline. Place your own components at the start, center or end, or adjust the built-in header with Timeline.HeaderListBuilder.remember().modify { } so that header items added in a future release still reach your integration. Timeline.Label.rememberTimecode, Timeline.Button.rememberPlayPause, rememberLoop and rememberToggleExpanded build the built-in items. Removing every header item removes the player bar with it.

  • Editor/Android: The timeline’s expanded state can now be driven by your own MutableState<Boolean> through the new expandedState property of Timeline. It sets the initial state and stays in sync as the user or your integration expands and collapses the timeline, because the expand/collapse toggle writes back to it. By default the editor owns the state, which starts expanded and survives configuration changes.

  • Editor/Web: The navigation bar and the inspector bar can now be placed below the canvas with cesdk.ui.setNavigationBarPosition('bottom') and cesdk.ui.setInspectorBarPosition('bottom'). Both default to 'top'. Call them after the editor configuration plugin, because resetEditor() restores the default placement.

AI Prompts & Reference Images

  • Editor/Web: Added builder.PromptInput() to render a prompt field in a registered panel. The field grows with its text, from three lines to ten, and carries no resize handle. Its attachments option adds a row of the images the prompt refers to. Each carries a remove button, beside a button that asks the panel for another one. Without that option the field renders alone.

  • Plugins/Web: The prompt takes as many reference images as the model’s schema allows. An image property of type array opens maxItems slots and receives a list, so a gateway model such as Seedream 4.5 takes ten images and FLUX.2 edit four. A string property, or an array whose schema names no limit, keeps one.

  • Plugins/Web: Added the Create Image from Reference quick action to the canvas menu of a selected image. It opens the image generation panel on the provider that owns the action, with the image attached to the prompt beside any images already there, and is disabled once the prompt has no room left. Every image-to-image provider offers it, and the feature key ly.img.plugin-ai-image-generation-web.quickAction.createImage turns it off.

Licensed Codecs

  • Engine/Node: Added support for licensed codec packs downloaded from the licensing dashboard on Linux x86_64. Packs are automatically detected in $HOME/.local/share/cesdk-codecs; set CESDK_LICENSED_CODEC_PACK to use a custom installation directory.

Improvements & Behavior Changes

Performance

  • Engine: A PDF export with exportPdfWithHighCompatibility disabled no longer holds a decoded copy of an image above maxImageSize in the image cache. Such an export needs less memory.

  • Engine/Web: CreativeEngine.init() is faster, and no longer hangs when a browser’s video codec does not answer.

  • Engine: Improved undo and redo performance in large scenes.

  • Engine: Improved emoji detection, text creation, shaping, and typing performance while reducing memory churn for long plain and wrapped text.

  • Engine: Improved scrolling performance.

  • Engine: An image is no longer decoded at full resolution to build a hit-test mask or to read its width and height. A scene with large photos now holds much less memory, and an export of it is faster.

Color Management & PDF Output

  • Engine: A PDF export now writes the TrimBox and the BleedBox of every page that has a bleed margin (page/margin). The MediaBox alone does not say where a print shop cuts. The TrimBox is the page frame and the BleedBox is the sheet around it. A page with no bleed gets no boxes, because its sheet is already the trim. A page whose bleed is not a rectangle gets no boxes either, because a PDF page box is a rectangle. That covers a page with a non-rectangular shape and a page using page/marginScale, whose cut line is a die line. No export changes size, and there is no new option to set.

  • Engine: CMYK and YCCK JPEGs without embedded profiles now use the document CMYK profile in managed scenes. Set scene/colorConversionMode to Managed to update an existing scene; Legacy preserves its previous appearance. Images with embedded profiles now have consistent colors in the editor and server-side renders.

Node Native Argument Validation

  • Engine/Node Native: block.export, block.exportAudio, block.addImage, block.applyAnimation and scene.zoomToBlock report an error for an argument of the wrong shape instead of acting on a coerced value. block.exportAudio accepts only (block, options), the signature it declares and the one the web bindings accept; a positional mime type is no longer taken. editor.setSetting on an Int setting requires a whole number instead of truncating one.

  • Engine/Node Native: block.export checks each option’s value, not only its type, and accepts the full option set the web bindings accept. pngCompressionLevel must be 0-9, and jpegQuality, webpQuality and pdfImageQuality must be above 0 and at most 1; the PDF and underlayer options are now declared, so a typo in one is reported instead of ignored. block.addImage accepts Absolute, Percent or Auto for sizeMode and positionMode, and reports any other value.

  • Engine/Node Native: block.export reports an error for an abortSignal instead of ignoring it. This binding cannot stop a running export, so accepting the option would have let code written against the web bindings run without ever aborting. onProgress is now declared too, so a misspelled name is reported rather than dropped in silence.

Editor Layout, Labels & Configuration

  • Engine/Android: Explicitly declare API 34 as minCompileSdk in AAR metadata. This preserves the existing consumer requirement when building with AGP 9.

  • Editor/Web: An unnamed page now reads with its number — “Page 1”, “Page 2” — matching the title the canvas draws, and a video track and a caption track read “Track” and “Caption Track” where both previously read “Graphic”. This is the fallback name cesdk.utils.generateBlockName returns, so it also shows in the inspector header and on a timeline clip.

  • Editor/Web: The dock in the design and advanced editor configurations ends with a spacer that holds the new button at the bottom, so a dock entry appended with position: 'end' lands below it. Pass before: 'ly.img.spacer' to keep a button up with the libraries.

  • Editor/Android: The timeline’s looping toggle moved from the center of the header to the end, next to the expand/collapse toggle. The play button now sits in the middle of the header.

  • Editor/Android: The timeline height is now expressed in track rows instead of pixels, through the new height property of Timeline. It grows to at most three tracks by default with TimelineHeight.Dynamic, or you can set a fixed number of tracks with TimelineHeight.Fixed.

  • Editor/Web: Removed the translation keys input.bleedMargin.select and property.bleedMargin. The panel labels the bleed with input.bleed and its four side inputs with input.sides.top, input.sides.right, input.sides.bottom and input.sides.left. Override those instead.

  • Editor/Web: Grid and safety margin controls now live in one “Grids & Guides” panel, opened from a row in the document and page inspectors.

  • Editor/Web: The navigation bar’s actions dropdown now has the accessible name and tooltip “Show more options”.

  • Editor/Web: resetEditor() no longer switches the engine’s video captions off, so a video configuration no longer has to switch them back on. The caption UI stays gated by the ly.img.video.caption feature.

AI Generation Panels

  • Plugins/Web: The input tabs of the AI image and video generation panels name the conversion: “Text to Image” and “Image to Image”, or “Text to Video” and “Image to Video”. Override them with the ly.img.plugin-ai-image-generation-web.fromText.label and .fromImage.label keys, or the matching video keys.

  • Plugins/Web: The input tabs no longer show an “Input” label, so the ly.img.plugin-ai-generation-web.fromType.label key is no longer used.

  • Plugins/Web: An AI generation panel now shows its reference image inside the prompt, rather than in a separate preview above it. An image selected on the canvas is offered in that row, outlined so it stands out on either theme, and attaches with one click. Selecting an image that is already attached highlights it.

  • Plugins/Web: CommonProperties.ImageUrl now renders the prompt as well as the image. A model whose schema has no image property keeps a prompt on its own.

  • Plugins/Web: The Text and Image switch in an AI generation panel is now a pair of tabs, not a segmented control. The arrow keys move between them, which the segmented control did not support.

  • Plugins/Web: Quick actions gain the new prompt field: Combine Images, Create Variant, Edit Image, Remix Page with Prompt, Swap Background and Change Text To.

  • Editor/iOS: The image style thumbnails in the AI image generation plugin no longer draw a border.

License Diagnostics

  • Engine: A license with a missing or wrong value now fails with LICENSE.CLAIM_INVALID, which names the value, instead of LICENSE.INVALID.

Bug Fixes

Engine

  • An archive no longer keeps a buffer:// URL for a resource it could not include. Such an archive pointed a block at a different file and crashed the engine when it was loaded. saveToArchive now reports SCENE.ARCHIVE_BUFFER_DATA_MISSING, and loading an archive that already holds one clears the reference.

  • saveToArchive now finishes when a referenced resource cannot be loaded. In the browser it returned a promise that never settled, so a download did nothing and gave no error. On the native engines it crashed the process. A missing image, an unreachable host, and an unloadable font in the settings all caused this. The archive keeps the original URI of such a resource.

  • A malformed block id inside an array is refused as well, so block.group([5.7, 8.7]) reports an error rather than grouping the blocks those values round to.

  • Combining blocks no longer shows a fill that the first block had hidden. The result now keeps the fill hidden, as it was.

  • A data: URI now loads on every platform. Only the browser accepted one before, because it passes the URI to the host rather than fetching it, so a scene that loaded in a browser failed to load on Node, Android, iOS, macOS and Windows.

  • block.getTypeface answers for a text block that was never given a typeface, on every binding. The engine bundles a default font but shipped no typeface declaring it, so the call reported that the default was not registered even though the font it names is present.

  • block.getTextFontWeights reports the weight the default font actually has. For a block with no typeface it previously answered normal regardless — a weight the bundled default, which is Semi Bold, does not have.

  • A data: URI now loads when its base64 payload is split over several lines or holds spaces. Such a URI gave an error, or an image built from only part of the data.

  • Undo, redo and applying a template no longer crash when the scene holds a page without a parent. Such a page is one that is not appended yet, or a duplicate made with attachToParent set to false. Page carousel mode no longer crashes on such a page either.

  • An image that is flipped both horizontally and vertically becomes axis-aligned again after a content-aware resize or a crop preset. The rotation could land just short of a full turn, which left the image mirrored and made the crop tool move the wrong edges.

  • The fill API now reports an unknown block as BLOCK.UNKNOWN instead of BLOCK.FILL_MISSING.

  • An image that was added or drawn while the canvas was zoomed out is no longer shown at that low resolution after zooming in. It is decoded again at the resolution the canvas needs.

  • Fixed a v4 license that failed to unlock the SDK when the license server added a field that the SDK does not read.

  • A locked block now holds what sits inside it. A child of a locked group or track cannot be dragged, nudged or resized on the canvas, and alignHorizontally, alignVertically, distributeHorizontally and distributeVertically skip it, where before only the block’s own lock counted. isAlignable and isDistributable give the same answer as those endpoints. Each block keeps its own flag, so unlocking a container leaves a child that was locked on its own still locked. The chain stops at the page and at the scene: a lock on either holds only the block that carries it. setTransformLocked and isTransformLocked are unchanged and still read and write a block’s own flag.

  • Resizing a block no longer moves the opposite edge when the handle is dragged back to the start size or against a movement constraint.

  • A cropping resize that the page movement constraint or an exclusion area stops no longer keeps scaling the content. The frame held its edge while the image behind it grew with the pointer.

  • A scene export no longer takes its size from a hidden block. A block that is hidden, on its own or inside a group, now changes neither the pixel size nor the content of an exported scene. The exported pixel dimensions get smaller for a scene that holds a hidden block outside its pages. A page export keeps its size from the page, so it is not affected.

  • A scene export no longer takes its size from a block that stays out of the export. A block with includedInExport set to false now changes neither the pixel size nor the content of an exported scene. A page export keeps its size from the page, so it is not affected.

  • A source set now keeps every entry. Two files of the same width are two entries, and only the first one stayed before.

  • The engine now reads the size of a source set entry that states a width and height of 0. Such an entry gave the engine no way to tell a small file from a large one, so it could load a large file into a small block.

  • Fixed SRT caption imports with non-ASCII UTF-8 text.

  • A text block on a path no longer draws its block background. The rectangle did not follow the curve. The setting stays, and the background draws again when the path is removed.

  • A video export of a page no longer hangs at the first frame when a block on another page is still loading. A block with an error on another page no longer cancels the export.

  • A video export of a page now draws only the blocks of that page, as an image export does. A block outside that page, for example on another page at the same place, no longer appears in the video.

  • A text getter no longer moves a running camera animation, and it costs 7× less while one runs. The text APIs still refresh the text before they read it, also when implicit updates are off.

  • Fixed a crash in restoreBuffers on macOS when the engine already held a buffer. The call now replaces the buffers as documented.

Engine (Web + Node)

  • A promise from engine.actions.run() that is discarded while the run is still in flight at engine.dispose() no longer surfaces as an unhandled rejection. Awaiting it still rejects with Engine has been disposed.

  • Web: A string with a lone UTF-16 surrogate, such as text cut in the middle of an emoji, no longer loses characters or breaks the scene. The engine stores U+FFFD in its place. A scene saved with such a string now loads again. A scene that an older release saved with such a string still fails to load. Node: A string with a lone UTF-16 surrogate no longer loses characters or breaks the scene. The engine stores U+FFFD in its place, as @cesdk/node-native does on Node.js and Deno.

Engine (Web + Node Native)

  • Node Native: A method that returns a promise now rejects when an argument is invalid, instead of throwing before the promise is created. A caller who wrote .catch() — which is what a promise-returning method invites — did not see the failure at all. Web: A method that returns a promise now rejects when an argument is invalid, instead of throwing before the promise is created. This affects asset.findAssets, the two addLocalAssetSourceFromJSON* methods, block.loadFromURL, block.loadFromArchiveURL, the two add*FileURIToSourceSet methods, scene.createFromImage, scene.createFromVideo, and the four editor URI and font helpers.

Engine/Web

  • A negative block id, or one above 4294967295, now reports the same error as any other malformed id. Both used to surface an internal message about “an argument of type unsigned int”.

  • A license error during initialization now rejects with its EngineError. Before, it was a WasmLoadError that reported missing assets.

  • editor.getSetting and editor.setSetting report an unknown key as a proper engine error, carrying the same code, hint and documentation link every other engine error has. They previously threw a plain Invalid setting key error with none of that. The check now asks the engine instead of consulting a built-in list of keys, so a setting the engine supports is no longer refused because the list had not been updated for it.

Engine/Node

  • A block id that is not a whole number in [0, 4294967295] is now refused. Such an id used to be converted rather than rejected, so it reached a different, existing block: getName(5.7) returned block 5’s name, getName(NaN) read block 0, and a setter wrote to the block it converted onto.

  • A failed license request during CreativeEngine.init no longer stops the Node process while the engine assets still load.

Engine/Node Native

  • actions.get(id) returns the function you registered instead of always undefined, so a host can invoke a registered action synchronously by reference as it can on the web bindings.

  • asset.fetchAsset now reports an error for an asset source that does not implement fetchAsset. It previously terminated the whole Node process, which no try/catch could prevent.

  • editor.setSafeAreaInsets keeps the value of every side you leave out. It previously reset an omitted side to zero.

  • An asset reaches a source’s applyAsset, applyAssetToBlock and applyAssetProperty callback without its empty optional fields, matching the web bindings. A callback that tested if (asset.credits) previously took that branch for an asset with no credits.

  • block.setTextFontSize and block.getTextFontSizes treat their unit option the way the web bindings do. Both boundaries carry the scene’s own font-size unit, so a value already in that unit is passed through untouched. They converted to and from points instead, so on a Pixel scene at 300 dpi setTextFontSize(text, 54, { unit: 'Pixel' }) stored 54 × 72 / 300, and every later read of the raw text/fontSize value — block.getTextRuns among them — was smaller than the web bindings’ by the same 4.17 factor.

  • asset.canManageAssets reports whether a source declared the deprecated canManageAssets flag, matching the web bindings. It previously probed the engine and answered a different question — whether assets could be added to or removed from the source — so a source added with addLocalSource reported true where the web bindings report false.

  • block.combine reports an unknown boolean operation as the same engine error the web bindings report, naming every operation it accepts. It previously refused the call from a list kept in the binding, with an error carrying none of the usual code, hint or documentation link, and the list had to be updated by hand whenever the engine learned a new operation.

  • CreativeEngine.init with an invalid license now rejects with an error that try/catch can handle. Before, it stopped the Node process.

Engine/Android

  • Fixed a crash in getAudioInfoFromVideo for a video with audio tracks. The call now returns the AudioTrackInfo list.

  • Fixed a crash in setCameraPreview when the mirroredState flow changes after the first frame. The orientation update now runs on the engine thread.

  • Video preview and export no longer fail with a decoder resource error for high-resolution videos, such as 4K phone recordings. A decoder that decodes slowly is no longer treated as stuck and rejected, and the low-latency decode preference no longer excludes hardware decoders that do not advertise the feature.

  • Fixed popping sounds when exporting videos with loud or overlapping audio.

  • Fixed audio being cut off at the end of video and audio exports.

Editor/Web

  • An action still running when the editor is disposed no longer logs an unhandled rejection.

  • An asset or asset source credit registered without a url now shows its name in the asset info popover instead of the literal text “name”.

  • A locked block no longer moves in the stacking order and no longer flips. Bring Forward, Send Backward and Flip in the canvas menu, and the Position controls in the inspector, now refuse it the way dragging it on the canvas already did.

  • A block inside a locked group counts as locked everywhere. It was possible to reorder, flip or align one, even though the group could not be moved.

  • The camera animation started by scroll.toPage and scroll.toBlock no longer throws Cannot read properties of null (reading 'setPositionX') when the editor is disposed mid-animation. The animation now stops instead.

  • ly.img.shape.options now gates only the shape option controls it names — points, inner diameter, sides, corner radius and line width. The Replace Shape and Edit Path rows keep their own flags, ly.img.replace.shape and ly.img.shape.edit, so enabling one of those is enough to show it, in the inspector panel’s Shape section and in the inspector bar’s Shape button alike. Previously all three were hidden together.

  • The text background controls are disabled for text on a path.

  • Fixed an issue where the measurement theme design tokens would not be parsed properly ended up breaking the layout of the asset library grid.

  • The video timeline no longer scrolls to a selected clip that is already visible. Selecting, dragging or trimming a clip no longer shifts it away from the pointer, and a long clip no longer snaps to its start. The timeline still scrolls to a clip that is selected while completely out of view.

  • As an Adopter, clicking a different placeholder after closing the replace panel opens the panel again. Before, it opened again only for the first placeholder that was selected.

  • The replace panel stays open while you move or resize the selected block on the canvas. Before, it closed when the drag started.

  • A library card dragged immediately after it is pressed now shows its thumbnail while it is dragged. It sometimes showed an empty box instead.

Editor/iOS + Android

  • Fixed an issue where auto captions could not transcribe video recorded with the camera or added without the video block kind.

Editor/iOS

  • Fixed the “None” image style in the AI image generation plugin showing a blank box instead of an icon in the style selector and the style picker sheet.

Editor/Android

  • Fixed InspectorBar.Button.rememberVoiceover() sharing its id with rememberMoveAsClip(). The button now uses InspectorBar.Button.Id.voiceover, so it can be addressed in InspectorBar.ListBuilder.modify and no longer collides with the move-as-clip button.

  • Fixed the timeline Play button not restarting playback from the beginning when the playhead is at the end and looping is off.

Editor/Flutter + RN

  • Fixed an issue on iOS where closing the editor kept it in memory and kept playing video and audio until the editor was opened again.

Camera/iOS

  • Fixed second-camera clips and photos appearing at the wrong time on the timeline when a session mixed single-camera and Dual Camera captures.

Plugins/Web

  • The Create Video quick action, now labelled Create Video from Reference, attaches the selected image to the video prompt beside any images already there, is disabled once the prompt has no room left, and opens the panel on the provider that owns the action. Before, the image was stored under the image provider’s id and never reached the video panel, and providers whose image property is not image_urls never received it.

  • A print-ready PDF export now finds its ICC profile in a bundled app.

Version v1.82.2 Changelog September 26, 2026

Breaking Changes

Non Breaking Changes

  • Engine: A license with a missing or wrong value now fails with LICENSE.CLAIM_INVALID, which names the value, instead of LICENSE.INVALID.
  • Engine/Web: A license error during initialization now rejects with its EngineError. Before, it was a WasmLoadError that reported missing assets.
  • Engine/Node: A failed license request during CreativeEngine.init no longer stops the Node process while the engine assets still load.
  • Engine/Node Native: CreativeEngine.init with an invalid license now rejects with an error that try/catch can handle. Before, it stopped the Node process.

Version v1.82.1 Changelog September 21, 2026

New Features

Licensed Codec Packs

  • Engine/Node: Added support for licensed codec packs downloaded from the licensing dashboard on Linux x86_64. Packs are automatically detected in $HOME/.local/share/cesdk-codecs; set CESDK_LICENSED_CODEC_PACK to use a custom installation directory.

Bug Fixes

Engine

  • Undo, redo and applying a template no longer crash when the scene holds a page without a parent. Such a page is one that is not appended yet, or a duplicate made with attachToParent set to false. Page carousel mode no longer crashes on such a page either.
  • A video export of a page no longer hangs at the first frame when a block on another page is still loading. A block with an error on another page no longer cancels the export.
  • A video export of a page now draws only the blocks of that page, as an image export does. A block outside that page, for example on another page at the same place, no longer appears in the video.

Engine (Web + Node)

  • A string with a lone UTF-16 surrogate, such as text cut in the middle of an emoji, no longer loses characters or breaks the scene. The engine stores U+FFFD in its place, as @cesdk/node-native does on Node.js and Deno. On Web, a scene saved with such a string now loads again. A scene that an older release saved with such a string still fails to load.

Version v1.75.2 Changelog September 15, 2026

Breaking Changes

Non Breaking Changes

  • Engine/Web: Applied critical fixes to tracking and licensing in the Web and Node.js SDKs.

Version v1.76.2 Changelog September 14, 2026

Breaking Changes

Non Breaking Changes

  • Engine/Web: Applied critical fixes to tracking and licensing in the Web and Node.js SDKs.

Version v1.82.0 Changelog September 10, 2026

Breaking Changes

  • Engine/Android: BlockApi.export takes a new optional progressCallback parameter after options. Calls that pass onPreExport or uriResolver positionally must switch to named arguments.

  • Editor/Web: Removed the legacyNonResponsivePanelPosition feature flag. Integrations that set it no longer force the wide layout.

  • Editor/Web: The video timeline controls bar is now driven by the Component Order APIs under the new ly.img.video.timeline.controls.bar UI area (cesdk.ui.setComponentOrder, getComponentOrder, insertOrderComponent, updateOrderComponent, removeOrderComponent). The built-in controls are registered as components (ly.img.video.timeline.background, .split, .playbackInfo, .playPause, .loop, .zoom, .toggle) and custom components can be added via cesdk.ui.registerComponent. A new ly.img.video.timeline.controls.bar Feature API flag gates the whole bar (parallel to ly.img.canvas.bar). Breaking: the SDK no longer ships a built-in order for this bar — without configuration the controls bar is empty and not rendered. Set the order explicitly to keep the previous controls, or pin the configuration below 1.82 with cesdk.setEditorCompatibilityVersion. The bar’s internal DOM also changed from a fixed three-column grid to a flat flex row driven by ly.img.spacer components, so custom CSS targeting the old internal group containers no longer applies. To restore the previous controls:

    cesdk.ui.setComponentOrder({ in: 'ly.img.video.timeline.controls.bar' }, [
    'ly.img.video.timeline.background',
    'ly.img.video.timeline.split',
    'ly.img.spacer',
    'ly.img.video.timeline.playbackInfo',
    'ly.img.video.timeline.playPause',
    'ly.img.video.timeline.loop',
    'ly.img.spacer',
    'ly.img.video.timeline.zoom',
    'ly.img.video.timeline.toggle'
    ]);

    See the v1.82 migration guide and the starterkit-video-editor-ts-web / starterkit-advanced-video-editor-ts-web repositories (and the full starter kit upgrade) for more info.

  • Editor/Web: The clip ellipsis button on timeline clips can now be hidden through a dedicated Feature API flag, ly.img.video.timeline.clip.menu. It is enabled by default — cesdk.feature.disable('ly.img.video.timeline.clip.menu') hides the button, cesdk.feature.enable(...) shows it again. The flag gates only the trigger; the clip context menu order set via cesdk.ui.setComponentOrder({ in: 'ly.img.video.clip.menu' }, ...) is unaffected. Breaking: integrators that maintain an explicit feature allowlist must add ly.img.video.timeline.clip.menu to keep the button visible (see the v1.82 migration guide; the starter kits list it in their features.ts).

  • Engine: A block that has only a preview image is no longer pending. It renders the preview, so an export finishes. Use setState to keep a block pending.

  • Engine: isCapabilitySupported now answers for the codecs this build really has. hevcEncode is false on Linux, Windows and Android, where the engine has no HEVC encoder. It was true before.

  • Engine/Node: Added videoCodec to the video export options, with VideoCodec.H264 and VideoCodec.HEVC. It defaults to H.264, so existing exports are unchanged. Check isCapabilitySupported('hevcEncode') first: where the platform has no HEVC encoder, the export fails instead of falling back.

  • Engine/Node: Fixed enableHEVC being ignored. It is deprecated; use videoCodec.

  • Engine: Fixed missing picture on HEVC exports with audio.

  • Engine: Fixed an export that was refused leaving the encoder busy, which failed every later export.

  • Engine/Web: VideoMimeType is 'video/mp4' only. 'video/quicktime' was in the type, but the engine has never written a QuickTime file and rejected it at run time. Use 'video/mp4'. QuickTime stays valid for media you import.

  • Engine/Swift: Removed TransitionType.none. To remove a clip’s transition, destroy the block returned by engine.block.getTransition(_:) — destroying detaches it automatically. Use removeTransition(_:) instead when you want to detach the transition but keep the block for reuse; it does not destroy the detached block.

  • Renderer: The non-assetless Docker containers now only ship a single asset tree instead of shipping all asset versions in subtrees of /opt/cesdk-renderer/assets like before. This reduces image size and matches other platforms. If you need access to other asset versions, you can download them from the documentation pages for self-hosting your own assets for the matching CE.SDK version and bundle them in your own Docker image built on top of the assetless variant.

Non Breaking Changes

  • Editor/Android: Add automatic caption generation. The new ly.img:plugin-auto-captions package adds a Generate Automatically action to the Add Captions sheet that transcribes the video’s speech into time-synced, styled captions. Register it with .then(::AutoCaptionsPlugin) { provider = GatewayTranscriptionProvider(apiKey = "sk_…") } to use ElevenLabs Scribe through the IMG.LY AI Gateway, or implement TranscriptionProvider to plug in any other speech-to-text service. Generation runs in a cancellable state and leaves the scene untouched when cancelled; captions land in a single undo step. Integrators who want to own the whole pipeline can replace AutoCaptionsPlugin.captionsGeneration with their own CaptionsGenerator and return their own SRT or VTT file — a provider stays required either way.

  • Editor/Android: Add caption editing to the video editor. Register the captions dock button to let users create captions manually, import SRT/VTT files, edit caption text and timing on a dedicated timeline lane, apply style presets, and fine-tune a selected caption from the caption inspector (Edit Captions, Style, Split, Format, Fill & Stroke, Text Background, Delete). While editing, an action bar above the keyboard merges, splits, adds, and deletes captions, and steps to the caption above or below; merging, adding, and deleting are also available by swiping a row. Return splits a caption at the cursor — or starts the next one at the end of the text — and Backspace at the start merges into the caption above, or deletes it when empty. Style presets and core styling — font, size, alignment, colors, and background — apply to every caption on the track. Captions are burned into the exported video by the engine.

  • Editor/Flutter: Added Swift Package Manager support to the imgly_editor plugin. Flutter 3.44 and later resolves the plugin via Swift Package Manager by default; CocoaPods keeps working on older Flutter versions.

  • Camera/Flutter: Added Swift Package Manager support to the imgly_camera plugin. Flutter 3.44 and later resolves the plugin via Swift Package Manager by default; CocoaPods keeps working on older Flutter versions.

  • Editor/Web: Added builder.Tabs() to switch between views inside a panel. Only the selected tab builds its content, each tab takes an optional icon, tooltip and isActive indicator, and the arrow keys, Home and End move between them.

  • Editor/Web: The animations panel shows In, Loop and Out as tabs rather than stacked sections, each marked when that group has an animation applied. The inspector’s Design/Placeholder switch and the caption panel’s Content/Style switch use tabs too.

  • Editor/iOS: Add an IMGLYEditor product to the IMGLYUI-swift Swift package. You can now depend on the editor on its own — the product the Starter Kit guides tell you to add — instead of pulling the whole IMGLYUI umbrella. import IMGLYEditor resolves against it directly.

  • Editor/Web: Bounded numeric controls in the inspector now have a minus and a plus button inside the input. A press steps the value and stops at the control’s limits, holding a button repeats the step, and a whole press becomes one undo step. Typing a value still works. The buttons are not tab stops, because the field’s Up and Down arrow keys already step.

  • Editor/Web: These controls replace their slider with a stepper: letter spacing, line height and paragraph spacing; polygon sides, star points, star inner diameter, line width and corner radius; cutout smoothing; and text background corner radius.

  • Editor/Web: These controls keep their field and gain the buttons: stroke width, cutout offset, grid width and height, shadow distance and blur, and text background horizontal and vertical padding.

  • Editor/Web: The font size control gains the same buttons and keeps its preset list. The list now opens on a click on the field, or with Alt+ArrowDown, and replaces the dropdown button beside it.

  • Editor/Web: Added a showStepper option to builder.NumberInput() that renders the same decrement and increment buttons.

  • Editor/Web: Added the translation keys input.stepper.change, input.stepper.decrease, input.stepper.increase, input.stepper.decreaseValue and input.stepper.increaseValue. The keys property.textBackground.horizontalPadding.description and property.textBackground.verticalPadding.description are gone, because the controls they described are steppers now and name their own buttons.

  • Engine: Added the pdfChunkSize export option, which sets the upper bound in bytes for a single chunk of PDF output. It matters most for a streamed export, where it is the memory the caller holds for one chunk: a larger value lowers the per-chunk overhead and raises that memory, a smaller value does the opposite. Leaving it unset keeps the engine default. A value outside the supported range is clamped into it rather than failing the export.

  • Engine: Added per-page progress reporting to PDF export on Web, iOS, and Android, mirroring video export progress. See the Export to PDF guide.

  • Editor/Web: The export dialog now shows a progress bar instead of a spinner while a multi-page PDF exports.

  • Editor/React Native: Added opt-in Swift Package Manager support to @imgly/editor-react-native for React Native’s experimental SPM mode (npx react-native spm, React Native 0.87 and later). CocoaPods currently remains the default integration path.

  • Editor/React Native: Fixed a crash on iOS when an export result contained no artifact, scene, or thumbnail — the result now carries null for the missing fields, matching Android.

  • Camera/React Native: Added opt-in Swift Package Manager support to @imgly/camera-react-native for React Native’s experimental SPM mode (npx react-native spm, React Native 0.87 and later). CocoaPods currently remains the default integration path.

  • Camera/React Native: Fixed a crash on iOS when openCamera received settings that could not be parsed — the call now rejects with error code E_PARSING instead.

  • Editor/Web: Added a Gap control to the document inspector that adjusts the spacing between the pages of a horizontally or vertically stacked scene, together with a Gap Unit dropdown that chooses whether the value is measured in Screen Pixels or in Design Units. Screen Pixels affects the canvas preview only — it keeps the gap constant on screen at any zoom level, while exports always interpret the gap in design units. Switching the unit never rewrites the number, so the same value is reinterpreted rather than converted. Both controls are shown only while a horizontal or vertical stack layout is active and the stack holds at least two pages — a stack’s spacing sits between its children, so there is nothing for it to do on a single page. The value is limited to 10000; the engine itself sets no upper bound, but a very large gap in Screen Pixels stays that size at every zoom level and puts the next page out of reach. Both rows can be hidden with the new ly.img.scene.layout.spacing feature flag.

  • Editor/Web: The scene layout buttons in the document inspector no longer reset the gap between pages. Previously, switching to a stacked layout — or clicking the already-active layout button — overwrote stack/spacing with a fixed 35 screen pixels, discarding any gap that had been set through engine.block.setFloat(stack, 'stack/spacing', …). The current gap is now carried forward instead, including across a round trip through the free layout, and 35 is used only when no stack exists to read a gap from. Whether this is visible depends on how the scene was created: a scene created through cesdk.actions.run('scene.create', …) with a page preset already starts at 35, so it looked the same before and after, while a scene created without a page preset starts at 48 and previously dropped to 35 on the first layout click. Either way, a gap your integration set through engine.block.setFloat(stack, 'stack/spacing', …) is no longer discarded.

  • Editor/Web: Switching a scene to the free layout now stores the stack gap on the scene in the metadata keys stackSpacing/spacing and stackSpacing/inScreenspace, so that selecting a stacked layout again restores it. Like the existing freeLayout/position* keys written by the same code path, these are visible to integrations through engine.block.findAllMetadata(scene) and are saved with the scene.

  • Engine/iOS: Added two BlockAPI.export overloads that deliver the exported PDF incrementally in bounded chunks instead of building the whole document in memory, so large multi-page documents such as photo books and magazines export without exhausting memory. One writes the bytes into a file as they are encoded, the other hands each chunk to a closure. Because the chunks are handed over synchronously, a slow destination throttles the encoder rather than buffering chunks in memory.

  • Editor/iOS: Added the exportToFile configuration helper, which fuses exporting and writing the result to a file into a single streamed pass so the full document is never held in memory.

  • Editor/Web: Added a timeline.setHeight action to control the video timeline’s height. Call cesdk.actions.run('timeline.setHeight', { height: 320 }) to give the timeline a fixed height, { height: 'auto' } to restore the default content-hugging behaviour, or { maxHeight: 400 } to cap how tall the auto-growing timeline may get.

  • Editor/Web: Added a timeline/transitionControlVisibility editor setting controlling the clip transition control on the video timeline. Set cesdk.engine.editor.setSetting('timeline/transitionControlVisibility', 'always') to keep the control shown, or 'hover' (the default) to reveal it on hover/focus.

  • Editor/Web: Added keyboard shortcuts for moving between editor regions:

    • F6 / Shift+F6 now also cycle through the timeline, the navigation bar, the dock, the canvas bars, and open panels.
    • Alt+4 — move focus to the timeline.
    • Alt+5 — move focus to the navigation bar.
    • Alt+6 — move focus to the dock.
    • Alt+7 / Alt+8 — move focus to the canvas bar above / below the canvas.
  • Editor/Web: Improved keyboard and screen-reader accessibility of the video timeline and the editor chrome:

    • Timeline clips are tab stops: Enter / Space selects a clip (Shift adds to the selection), and screen readers announce the clip’s name, duration, and selection state.
    • The playhead is an operable slider: arrow keys step, Shift+Arrow, PageUp / PageDown take larger steps, Home / End jump to the start and end. Clicking it no longer moves keyboard focus.
    • The trim inspector, the trim handles, and the timeline resize handle can be operated with the arrow keys.
    • Popover menus such as the clip context menu navigate with the arrow keys, and the clip context-menu button has an accessible name.
    • The navigation bar, dock, canvas bars, inspector bar, and panels are exposed as ARIA landmarks.
    • Panels no longer trap keyboard focus. A panel opened from a control moves focus into it and returns focus to that control or block when it closes.
    • A collapsed timeline and hidden UI elements (an empty inspector bar, covered stacked panels) are skipped by keyboard navigation.
    • Focus rings are no longer clipped by scrolling containers and no longer appear after pointer interactions.
  • Engine/Web, Engine/Node: The default typeface assets now ship as WOFF2 instead of TTF. The font files are about 66% smaller, so editors load fonts faster on slow connections. Scenes that reference the previous TTF files continue to work.

  • Engine/Web, Engine/Node: A PDF export that uses a WOFF2 font embeds the glyph outlines instead of the font program. This already applied to WOFF and variable fonts, and now also applies to the default typefaces. The rendered output and the text search stay the same.

  • Editor/Web: The cutout inspector panel now shows the Offset and Smoothing controls again.

  • Editor/Web: Fixed an issue where the highlighted option in dropdowns could scroll out of view while navigating with the Arrow Up and Arrow Down keys.

  • Engine: Cancelling a PDF export now stops it. Before, an export that was cancelled kept rendering every remaining page and only the result was dropped. On Web an abortSignal stops the export, on iOS the cancellation of the enclosing Task, and on Android the cancellation of the coroutine. A cancelled export fails with ENCODE.EXPORT_CANCELLED.

  • Engine: The licensed codec concurrency limit now only applies when video or audio codecs are actually used. Image-only workloads no longer reserve a codec license slot, and a video operation fails with a clear error when all slots are in use.

  • Engine/Node Native: Engine creation no longer stops the JavaScript event loop while the license server activates an API key. This applies to the native bindings only.

  • Editor/Flutter: Fixed an issue where effects applied to paused videos were not immediately visible when using TextureView.

  • Editor/Web: A font size applies to the selected part of a text, whether stepped, typed or picked from the list.

  • Editor/iOS: A video picked from the photo roll now keeps its own duration instead of being added as a 5 second clip. The duration is read from the video when it is imported, so it also shows on the asset library tile.

  • Engine: relocateResource now moves every reference to a resource, also the references that carry a URL fragment. A variable font keeps its weight after the move.

  • Engine: A saved .imgly archive stays editable after you load it and move its resources. Before, the scene reported a buffer URL and could not be saved.

  • Editor/Web: Fixed an issue in Safari where edge artifacts on asset library cards remained visible on low pixel density displays, even without hovering.

  • Engine: Saving to a string with a resource callback no longer hangs, and no longer reports the resources of an earlier save.

  • Engine: Saving a scene that nothing has edited now gives the same bytes every time. Before, the blocks, the selection, the block metadata, the spot colours and the template variables were written in an internal order that moved on every load, so hashing a scene to detect a change, content-addressed storage and ETag caching all reported a change that had not happened. The saved content was never affected.

  • Engine: Thin seams no longer appear on the canvas between opaque blocks that share a straight, axis-aligned edge.

  • Engine/Swift: Fixed ExportOptions.webpQuality being ignored by exportWithColorMask. WebP exports with a color mask now use the given quality instead of the default.

  • Engine/Swift: Deprecated LicenseError.missing. An empty license key activates evaluation mode instead of failing, so this error is never thrown. Exhaustive switch statements over LicenseError keep compiling unchanged.

  • Editor/Android: Fixed dragging a clip into a gap narrower than the clip on an overlay track moving that clip and its left-hand neighbour to the start of the timeline. The clip now lands right after its neighbour and pushes the following clips later.

  • Editor/iOS: Fixed dragging a clip into a gap narrower than the clip on an overlay track moving that clip and its left-hand neighbour to the start of the timeline. The clip now lands right after its neighbour and pushes the following clips later.

  • Engine/Web/Node: editor.setURIResolver() and editor.setURIResolverAsync() now remove the resolver when you give them null or undefined. Before, the resolver stayed active.

  • Engine: Exporting a still image of a block containing a video fill now waits for the frame at the current playback time to be decoded. Previously the export could return the previously decoded frame, so stills taken at different playback times (timeline scrubbing, thumbnail-at-time) could come back identical.

  • Editor/Web: Fixed an issue where the Animations control and the “Allow to Change Animations” placeholder option were enabled in design-based starterkits.

  • Engine/Linux: Improved compatibility with H.265 video files in the licensed codec pipeline.

  • Engine/Swift: Export results no longer copy the whole payload when they cross into Swift. This lowers the peak memory of image, PDF, video, audio and color mask exports by the size of one result.

  • Engine/Node: Export results no longer copy the whole payload when they cross into JavaScript, which lowers the peak memory of every export by the size of one result. Applies to @cesdk/node-native.

  • Engine: Fixed a video export result becoming invalid when a later export started. Keeping the data of one export while starting the next one now works.

  • Engine/Node: A PDF export no longer builds the whole document in memory. The engine writes it to a file in the temp directory while the export runs, so the peak no longer grows with the number of pages. Large multi-page documents such as photo books and magazines export without exhausting memory. The result of export is unchanged.

  • Engine/Node: Platform capabilities are now detected while the engine starts. Before this, every capability reported unsupported, so features that ask for one, such as the staged export above, never ran.

  • Engine/Web: PDF exports via engine.block.export() are now written to a temporary file (origin private file system) while the export runs, instead of being assembled in memory, on browsers that support it. Exporting a large document no longer holds the finished file in memory and the returned Blob is backed by that file. Its content and type are unchanged. Browsers without support keep the previous behavior. The temporary file backs the returned Blob, so it stays on disk after the export and is collected automatically.

  • Engine/Android, Engine/Swift: PDF exports are now written to a temporary file while the export runs, instead of being assembled in memory. Exporting a large document no longer holds the finished file in memory. The exported bytes are unchanged. The temporary file is removed as soon as the export result is released.

  • Editor/Web: Softened the light theme’s hover fill (--ubq-interactive-hover), which held the same colour as the input background. Buttons, asset cards, select options, checkboxes and the timeline clip seam all hover differently now.

  • Editor/Web: Added the --ubq-interactive-input-hover theming token, the hover fill for elements sitting on an input background.

  • Editor/iOS: Added UI support for transitions, including SheetTypes.Transition, SheetType.transition(style:id:), InspectorBar.Button.transition(...), and timeline controls.

  • Editor/iOS: Added animation and transition previews for a better editing experience.

  • Editor/Web: cesdk.setEditorCompatibilityVersion('1.82.0') pins an editor configuration to the CE.SDK version it was written for, so listed behavior changes from later versions do not apply to it. Call it after cesdk.resetEditor(), which clears any previous pin. The CE.SDK editor configurations and starter kits pin it for you; a configuration that does not pin gets the current release’s behavior.

  • Editor/Web: ly.img.shape.options now gates only the shape option controls it names — points, inner diameter, sides, corner radius and line width. The Replace Shape and Edit Path rows keep their own flags, ly.img.replace.shape and ly.img.shape.edit, so enabling one of those is enough to show it, in the inspector panel’s Shape section and in the inspector bar’s Shape button alike. Previously all three were hidden together.

  • Engine: An opaque page/marginFillColor now covers the bleed content of a rectangular page at every zoom level. A thin line of the content stayed visible at the outer bleed edge before, and it appeared and disappeared as the canvas was zoomed or panned.

  • Engine: Fixed engine stalling on end-to-end video exports.

  • Engine: Fixed re-exporting a page on the same engine producing a static clip.

  • Engine/Web, Engine/Node: The default typeface assets now ship as WOFF2 instead of TTF. The font files are about 66% smaller, so editors load fonts faster on slow connections. The typefaces, their names, and the directory layout do not change.

  • Engine/Web, Engine/Node: A scene that names a font file from an older asset version still works. The engine reads the current file name from the registered typeface source, and every published asset version keeps serving its own files. A scene that stores a font path resolved against the asset bundle you serve needs a one-time migration. The “To v1.82 (Fonts)” upgrade guide shows how to find and rewrite those paths.

  • Engine/Web, Engine/Node: A PDF export that uses a WOFF2 font embeds the glyph outlines instead of the font program. This already applied to WOFF and variable fonts, and now also applies to the default typefaces. The rendered output and the text search stay the same.

  • Renderer: A scene that names a font file from an older asset version now exports. The renderer registers the asset sources of its bundled asset tree, so the engine reads the current file name from them. Before, the export was cancelled with FILE_FETCH_FAILED.

Version v1.81.1 Changelog August 26, 2026

Breaking Changes

Non Breaking Changes

  • Engine/Swift: Revert the video export cancellation fix introduced in 1.81.0, which could crash the app when a video export was cancelled.

Version v1.81.0 Changelog August 24, 2026

Breaking Changes

  • Engine/Android: Removed TransitionType.None. To remove a transition, destroy the transition block returned by engine.block.getTransition(...).

Non Breaking Changes

  • Engine/Android, Editor/Android: Added portable CE.SDK Android agent skills for documentation, explanations, and project scaffolding with Kotlin and Jetpack Compose.
  • Editor/Android: Added UI support for transitions, including the SheetType.Transition sheet type, InspectorBar.Button.rememberTransition, and buttons in the Timeline.
  • Editor/Android: Added animation and transition previews for a better editing experience.
  • Engine/Web/Android/iOS: scene.saveToArchive accepts a compression option for the scene inside the archive.
  • Plugins/iOS: Add the IMGLYPluginAutoCaptions plugin for automatic caption generation in the video editor. Registering AutoCaptionsPlugin adds a primary “Generate Automatically” action to the Add Captions sheet that transcribes the scene’s audible content and creates styled, time-synced captions. The built-in GatewayTranscriptionProvider runs the ElevenLabs Scribe v2 speech-to-text model through the IMG.LY AI Gateway using an IMG.LY API key; any other speech-to-text service can be integrated by implementing TranscriptionProvider.
  • Editor/iOS: Add EditorConfiguration.Builder.captionsGeneration(_:) to back the Add Captions sheet’s “Generate Automatically” action with a custom caption generation callback.
  • Editor/iOS: Add caption editing to the video editor. Register the Dock.Buttons.captions() dock button to let users create captions manually, import SRT/VTT files, edit caption text and timing on a dedicated timeline lane, apply style presets, and fine-tune a selected caption from the caption inspector (Edit Captions, Style, Split, Format, Fill & Stroke, Text Background, Delete). While editing, an action bar above the keyboard merges, splits, adds, and deletes captions, and steps to the caption above or below; merging, adding, and deleting are also available by swiping a row. Return splits a caption at the cursor — or starts the next one at the end of the text — and Backspace at the start merges into the caption above, or deletes it when empty. Style presets and core styling — font, size, alignment, colors, and background — apply to every caption on the track. Captions are burned into the exported video by the engine.
  • Engine/Swift, Editor/iOS: Added portable CE.SDK Swift agent skills for documentation, explanations, and project scaffolding across iOS, macOS, and Mac Catalyst.
  • Editor/Web: Transitions can now be opened from the inspector bar and the inspector panel.
  • Engine: Scenes and archives are now compressed with Zstd by default, making saved scenes far smaller and both saving and loading faster. Saved scene strings change shape: they carry a UBQ2 prefix instead of UBQ1. Loading accepts either, so existing scenes keep working.
  • Editor/Android: Fixed the background removal plugin decoding the source image on the main thread, which blocked the UI and triggered a StrictMode DiskReadViolation.
  • Editor/Android: Fixed the editor reading file modification times on the main thread when loading file-based images, which triggered a StrictMode DiskReadViolation.
  • Camera/Android: Fixed the photo preview reading the captured file’s modification time on the main thread, which triggered a StrictMode DiskReadViolation.
  • Engine/Android: HDR videos (HDR10/PQ and HLG) are now tone-mapped to SDR during decoding, fixing severely incorrect colors in preview and export. This uses the HDR-to-SDR tone-mapping request introduced in Android 12, and only takes effect where the device’s video decoder supports it — in practice recent Qualcomm/Snapdragon hardware decoders.
  • Engine/Android: Fixed a StrictMode LeakedClosableViolation caused by the internal OkHttp cache when a network fetch returned an error response (e.g. HTTP 404).
  • Editor/Android: the Resize sheet no longer shows the Crop/Cover/Fit content fill mode picker, honoring SheetType.Crop.Mode.ResizeAll.hasContentFillMode = false and matching iOS
  • Editor/Android: Fixed shareFile resolving the file’s content uri on the main thread, which triggered a StrictMode DiskReadViolation when sharing an exported file. The uri is now resolved on a background thread and any failure is surfaced via the error state instead of being thrown.
  • Engine: Fixed saveToArchive leaving remote URLs in the saved scene instead of bundling the referenced bytes.
  • Engine: Fixed variable-font handling in scene and block archives.
  • Engine: Fixed a crash in audio waveform thumbnail generation. The crash could happen once the last requested sample was delivered or when the audio block was destroyed from within the chunk callback.
  • Engine: Scene files no longer store the basePath setting. A basePath stored by an older version is ignored on load.
  • Editor/Flutter: Fixed an issue where effects applied to paused videos were not immediately visible when using TextureView.
  • Engine/Web: Fixed scene.getMode() throwing a TypeError instead of returning null when no scene exists.
  • Engine: A text or caption style preset no longer replaces the text of a block that already exists. A preset writes its own text only when it makes a new block.
  • Engine: PDF export with exportPdfWithHighCompatibility: false now embeds photos as their original JPEG data if possible, drastically reducing export time and file size of image-heavy documents such as photo books.
  • Engine: Added the pdfImageQuality export option. Values below the default of 1.0 encode rasterized images (e.g. with effects applied) as lossy JPEG instead of lossless. Rasterized content that keeps transparency stays lossless, because JPEG has no alpha channel.
  • Engine/Swift: Fixed cancelling a video export leaving the device encoding the whole video. BlockApi.exportVideo suspended on the engine callback and ignored task cancellation, so dropping the returned stream reported the export as cancelled while the background export engine kept rendering and encoding every remaining frame, holding CPU, GPU and battery until it finished. Cancelling now stops the export and releases the background engine.
  • Camera/Android: Fixed the photo/video mode toggle ignoring taps in mixed capture mode. The selection indicator briefly moved to the tapped icon and then snapped back without switching the capture mode.
  • Engine/Linux: Improved compatibility with H.265 video files in the licensed codec pipeline.

All Releases

v1.82.3 Changelog October 18, 2026
v1.84.0 Changelog October 15, 2026
v1.83.0 Changelog October 1, 2026
v1.82.2 Changelog September 26, 2026
v1.82.1 Changelog September 21, 2026
v1.75.2 Changelog September 15, 2026
v1.76.2 Changelog September 14, 2026
v1.82.0 Changelog September 10, 2026
v1.81.1 Changelog August 26, 2026
v1.81.0 Changelog August 24, 2026
v1.80.0 Changelog August 13, 2026
v1.79.0 Changelog July 28, 2026
v1.78.0 Changelog July 17, 2026
v1.77.1 Changelog July 9, 2026
v1.77.0 Changelog July 2, 2026
v1.76.1 Changelog June 17, 2026
v1.76.0 Changelog June 3, 2026
v1.75.1 Changelog May 21, 2026
v1.75.0 Changelog May 19, 2026
v1.74.2 Changelog May 15, 2026
v1.74.1 Changelog May 7, 2026
v1.74.0 Changelog May 5, 2026
v1.73.1 Changelog April 23, 2026
v1.73.0 Changelog April 20, 2026
v1.72.3 Changelog April 14, 2026
v1.72.2 Changelog April 9, 2026
v1.72.1 Changelog April 2, 2026
v1.72.0 Changelog April 2, 2026
v1.71.0 Changelog March 19, 2026
v1.70.1 Changelog March 13, 2026
v1.70.0 Changelog March 9, 2026
v1.69.0 Changelog February 23, 2026
v1.68.0 Changelog February 6, 2026
v1.67.0 Changelog January 22, 2026
v1.66.1 Changelog December 19, 2025
v1.66.0 Changelog December 17, 2025
v1.65.0 Changelog December 5, 2025
v1.64.0 Changelog November 21, 2025
v1.63.1 Changelog November 13, 2025
v1.63.0 Changelog November 7, 2025
v1.62.1 Changelog November 7, 2025
v1.62.0 Changelog October 22, 2025
v1.61.0 Changelog October 8, 2025
v1.60.0 Changelog September 19, 2025
v1.59.2 Changelog September 12, 2025
v1.59.1 Changelog September 8, 2025
v1.59.0 Changelog September 2, 2025
v1.58.0 Changelog August 19, 2025
v1.57.1 Changelog August 15, 2025
v1.57.0 Changelog August 6, 2025
v1.56.0 Changelog July 24, 2025
v1.55.2 Changelog July 18, 2025
v1.55.1 Changelog July 11, 2025
v1.55.0 Changelog July 8, 2025
v1.54.1 Changelog July 2, 2025
v1.54.0 Changelog June 26, 2025
v1.53.0 Changelog June 12, 2025
v1.52.0 Changelog May 29, 2025
v1.51.0 Changelog May 19, 2025
v1.50.2 Changelog May 15, 2025
v1.50.1 Changelog May 9, 2025
v1.50.0 Changelog April 29, 2025
v1.49.1 Changelog April 21, 2025
v1.49.0 Changelog April 16, 2025
v1.48.1 Changelog April 11, 2025
v1.48.0 Changelog April 1, 2025
v1.47.0 Changelog March 20, 2025
v1.46.1 Changelog March 5, 2025
v1.46.0 Changelog March 4, 2025
v1.45.0 Changelog February 19, 2025
v1.10.6 Changelog February 19, 2025
v1.44.0 Changelog February 6, 2025
v1.43.0 Changelog January 21, 2025
v1.42.0 Changelog January 9, 2025
v1.41.1 Changelog December 16, 2024
v1.41.0 Changelog December 11, 2024
v1.40.1 Changelog December 6, 2024
v1.40.0 Changelog November 28, 2024
v1.39.0 Changelog November 12, 2024
v1.38.0 Changelog October 29, 2024
v1.37.0 Changelog October 14, 2024
v1.36.1 Changelog October 4, 2024
v1.36.0 Changelog September 30, 2024
v1.35.1 Changelog September 20, 2024
v1.35.0 Changelog September 16, 2024
v1.34.0 Changelog August 31, 2024
v1.10.5 Changelog August 30, 2024
v1.33.0 Changelog August 23, 2024
v1.10.4 Changelog August 21, 2024
v1.32.0 Changelog August 6, 2024
v1.31.0 Changelog July 18, 2024
v1.30.0 Changelog July 3, 2024
v1.29.0 Changelog June 20, 2024
v1.28.0 Changelog June 6, 2024
v1.27.1 Changelog May 23, 2024
v1.26.1 Changelog May 22, 2024
v1.27.0 Changelog May 21, 2024
v1.26.0 Changelog May 7, 2024
v1.25.0 Changelog April 23, 2024
v1.24.0 Changelog April 5, 2024
v1.23.0 Changelog March 26, 2024
v1.22.0 Changelog March 12, 2024
v1.21.1 Changelog February 27, 2024
v1.21.0 Changelog February 22, 2024
v1.20.0 Changelog January 25, 2024
v1.19.0 Changelog December 13, 2023
v1.18.1 Changelog November 29, 2023
v1.18.0 Changelog November 6, 2023
v1.10.3 Changelog October 26, 2023
v1.10.2 Changelog October 26, 2023
v1.17.0 Changelog October 9, 2023
v1.16.1 Changelog September 14, 2023
v1.16.0 Changelog September 8, 2023
v1.15.0 Changelog September 1, 2023
v1.14.0 Changelog July 21, 2023
v1.13.1 Changelog July 10, 2023
v1.13.0 Changelog July 3, 2023
v1.12.2 Changelog June 30, 2023
v1.12.1 Changelog June 12, 2023
v1.12.0 Changelog June 2, 2023
v1.11.1 Changelog May 17, 2023
v1.11.0 Changelog May 10, 2023
v1.10.1 Changelog March 29, 2023
v1.10.0 Changelog March 14, 2023
v1.9.2 Changelog January 18, 2023
v1.9.1 Changelog December 15, 2022
v1.9.0 Changelog December 12, 2022
v1.8.0 Changelog October 21, 2022
v1.7.0 Changelog August 11, 2022
v1.6.3 Changelog June 13, 2022
v1.6.2 Changelog May 24, 2022
v1.6.1 Changelog May 23, 2022
v1.6.0 Changelog May 16, 2022
v1.5.1 Changelog May 9, 2022
v1.4.7 Changelog May 3, 2022
v1.4.6 Changelog April 14, 2022
v1.5.0 Changelog April 4, 2022
v1.4.5 Changelog April 1, 2022
v1.4.4 Changelog March 9, 2022
v1.4.3 Changelog March 7, 2022
v1.4.2 Changelog February 23, 2022
v1.4.1 Changelog February 22, 2022
v1.4.0 Changelog February 18, 2022
v1.3.0 Changelog January 6, 2022
v1.2.1 Changelog November 30, 2021
v1.2.0 Changelog November 22, 2021
v1.1.1 Changelog October 29, 2021
v1.1.0 Changelog October 28, 2021
v1.0.0 Changelog August 18, 2021