Search Docs
Loading...
Skip to content

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.