Minor Changes
-
#2617
1c9fb43Thanks @danielmlr! - AddsContentSaveRejectedErrorso acontent:beforeSavehook can reject a save with a message for the editor. Throwing it from a trusted plugin makes the content API respond with a structuredSAVE_REJECTEDerror (HTTP 422) that carries the message, and the admin shows it in the save and autosave toasts. Any other exception thrown by the hook still cancels the save, but now returns a genericCONTENT_HOOK_ERRORresponse instead of escaping as an unstructured 500 that could leak exception details. A plugin running in the sandbox cannot reject a save yet; the save proceeds as before, and the sandbox log states that a sandboxed plugin cannot cancel a save. Moving the plugin intoplugins: []runs it in the host process, where rejection works. The generated OpenAPI document lists the 422 response on content create and update. -
#2765
9d92b55Thanks @ascorbic! - Updates plugin publishing to host package bundles, icons, banners, and screenshots as blobs on the publisher's Personal Data Server by default. Runemdash-plugin publishfrom the plugin directory; the CLI builds the bundle, checks the stored OAuth grant, uploads the artifacts, and writes CID-bound checksums into the release record.Existing scripts can keep externally hosted package bundles with
emdash-plugin publish --url <https-url>. The CLI still downloads that URL to validate and hash the served bytes. Listing images are uploaded as publisher blobs on both paths.The experimental aggregator release envelope replaces
mirrorswith typedartifactCaches. The field is optional during rolling upgrades, and updated clients treat an omitted field as an empty cache list. A record-scoped cache descriptor supplies its service endpoint; clients derive/r/{did}/{collection}/{rkey}/{recordCid}/{blobCid}so cache admission is bound to the exact release revision.Install and update verify raw cache, PDS, and external fallback bytes against the signed checksum and blob metadata. The authenticated image proxy may serve a transformed record-scoped cache rendition; if that cache is unavailable, it falls back to checksum-verified PDS or external bytes. Listing images remain capped at 1 MiB.
Sites must upgrade EmDash before installing a release whose package artifact is available only as a PDS blob. Older EmDash versions require an external package URL.
What should I do?
Remove
--artifact-base-urlfrom publish scripts and stop pre-uploading listing images. The CLI rejects the removed option with migration guidance. Replace any experimentalreleaseView.mirrorsaccess withreleaseView.artifactCaches ?? []. If an existing granular login reportsMISSING_BLOB_SCOPE, runemdash-plugin logoutand log in again to grantblob:application/gzipandblob:image/*. -
#2582
8d8d3deThanks @khoinguyenpham04! - Adds numbered page navigation and page-size controls to the local Media Library. Media list requests can opt into numbered pages withpageand receive an exacttotalCount; cursor pagination remains the default.MediaLibraryaccepts controlled numbered pagination throughpagination. ExistinghasMoreandonLoadMoreprops remain supported whenpaginationis omitted. -
#2622
089d747Thanks @khoinguyenpham04! - Adds Lua and Zig to code block language selectors in the admin and inline visual editors. Lua code is syntax highlighted, while Zig uses the existing plain-text fallback. -
#2584
a1ddcfbThanks @khoinguyenpham04! - Adds flat media folders to theemdashREST and typed client APIs. Existing media remains in the
Main library after upgrade, so sites do not need to migrate media assignments.- Includes database migration
072_media_folders.
Use
GETorPOST /_emdash/api/media/foldersto list or create folders, andPUTor
DELETE /_emdash/api/media/folders/:idto rename or delete them. Folder reads require
media:read; folder writes requiremedia:edit_any. Typed clients expose the corresponding
mediaFolderList,mediaFolderCreate,mediaFolderUpdate, andmediaFolderDeletemethods.Media list requests can filter by a folder ID or use
folderId=unfiledfor the Main library.
Media updates andmediaSetFolderaccept a folder ID,null, orunfiled. Authors can assign
their own media, while editors can assign any media.Deleting a folder returns its media to the Main library without changing media IDs, storage keys,
URLs, or usage records. - Includes database migration
-
#2609
d379d10Thanks @danielmlr! - Adds an optional dark mode counterpart to image fields, so editors can pick a second image that the site shows in dark color schemes.Enable the slot per field with the
darkVariantwidget option ("options": { "darkVariant": true }in a seed file, or the Dark mode variant switch in the admin field editor). Editors then see Add dark mode variant below the selected image. The variant is stored inside the field value asdarkVariant, in the same shape as the primary image.The
Imagecomponent fromemdash/uirenders both images when a variant is present and shows the matching one with CSS: adarkorlightclass on<html>pins the scheme, otherwiseprefers-color-schemedecides. Both images share the primary image's alt text and loading attributes, and anidyou pass lands on the primary image while the variant gets it with a--darksuffix. Withoutpriority, the hidden one stays lazy and is not fetched until the scheme changes; withpriority, both images download. Sites with another theme convention can override the.emdash-image--lightand.emdash-image--darkselectors; the Dark Mode guide shows the rules. Fields without the option, and values without a variant, render exactly as before. -
#2480
b4c73acThanks @DavidPivert! -LiveSearchnow scopes its results to the locale of the page it is used on.Behaviour change. The component reads
Astro.currentLocaleand forwards it as thelocalequery parameter to/_emdash/api/searchand/_emdash/api/search/suggest, which both already filtered on it. Until now the component never sent one, so a translated site got every entry back once per language — a French visitor searching from a French page saw each result twice, the second one opening its English page. Sites withi18nconfigured that relied on searching across every locale will see fewer results than before.Two ways to opt out: pass an explicit
localeto search a different one, orlocale={null}to search across every locale, which is the previous behaviour.<!-- results in the page's own locale (new default) --> <LiveSearch collections={["posts", "pages"]} /> <!-- every locale, as before --> <LiveSearch collections={["posts", "pages"]} locale={null} />
Sites without Astro's
i18nconfigured are unaffected:Astro.currentLocaleisundefinedthere, so nolocaleparameter is sent and the search still spans everything. -
#2624
436f63dThanks @khoinguyenpham04! - Adds focal points for local images so cover-cropped thumbnails, galleries, and image components keep the selected subject visible. -
#2586
815553cThanks @khoinguyenpham04! - Adds flat-folder organization to the local Media Library. Editors can create, rename, and delete folders. Authors can organize their own local media, and editors can organize any local media, through Media Details or by dragging a media card or row onto a visible folder.Uploads continue to enter the Main library. Deleting a folder returns its media to the Main library without deleting files or changing their URLs.
-
#2538
9c52b39Thanks @khoinguyenpham04! - Adds a media usage tracking setting. Tracking is enabled during initial setup. Existing sites enable it from Settings, keep the page open while EmDash scans existing content, and can return later to continue from saved progress. -
#2106
f00174bThanks @gruntlord5! - Updatessqlite()and the EmDash CLI to use Node.js's built-in SQLite driver. Node.js deployments can install and run EmDash without compiling or downloading thebetter-sqlite3native add-on. Existing SQLite database files andsqlite({ url })configuration continue to work.This release requires Node.js 22.16 or later. Check the installed version with
node --versionand upgrade Node.js before updating EmDash if it reports an earlier release.Node.js 22 prints its standard
ExperimentalWarning: SQLite is an experimental featuremessage when the SQLite adapter loads. Node.js 24 does not print this warning.If
better-sqlite3is listed in the site'spackage.jsononly for EmDash, remove that dependency after updating. -
#2802
bfdaccdThanks @khoinguyenpham04! - AddshandleMediaUsageActivationAdvance,handleMediaUsageProgress, andhandleMediaUsageRepairto theemdashpackage root so integrations can activate Media Usage, rebuild usage data, and check when indexing is ready. Playground sessions use these handlers soUsed inresults are ready when the admin opens. -
#2538
9c52b39Thanks @khoinguyenpham04! - Removes scheduled media usage recovery. Media usage tracking now advances only while an administrator keeps its settings page visible. This breaks Cloudflare deployments that configuremediaUsageCronand Node.js integrations that provide a customCronScheduler.What should I do?
On Cloudflare, remove the dedicated media usage Cron Trigger and the
mediaUsageCronoption. Keep the general Cron Trigger unchanged; no replacement trigger is required.If you provide a custom Node.js scheduler, remove
setMediaUsageMaintenance(). A customCronSchedulernow implements onlystart(),stop(),reschedule(), andsetSystemCleanup().Keep Settings → Media usage tracking open until it shows Ready. If the page closes, return to continue from saved progress.
-
#2647
e3ad082Thanks @ascorbic! - Adds signed-label policy and listing-status support to the plugin registry client. Registry requests use the aggregator's required listing policy with an optional accepted-labeler declaration, and withdrawn releases are excluded from install and update results.The EmDash admin waits for a fresh listing-policy response before rendering registry metadata, uses the approved author name or publisher DID instead of a mutable handle, and does not request media for an unapproved release. Install, update, and media-proxy checks enforce listing withdrawal independently from the existing plugin-code and capability checks.
Registry artifact downloads and proxied media connect only to the public IP addresses validated for each URL, preventing DNS changes between validation and connection from reaching private services.
Patch Changes
-
#2683
3e90689Thanks @hossein-webdev! - Fixes AVIF images being rejected with "File type not allowed" on upload.image/avifis back in the default media allowlist alongside PNG, JPEG, GIF, and WebP, so editors can upload.aviffiles again from the media library and from image fields that use the default allowlist.The admin file picker now offers
.aviffiles and renders their thumbnails, the built-in "Images" preset in a field's allowed-types editor includes AVIF, and.avifworks as extension shorthand in a field'sallowedMimeTypes.SVG stays excluded from the default allowlist.
-
#2606
22c4422Thanks @khoinguyenpham04! - Fixes code blocks in the admin and inline visual editors with syntax highlighting for supported languages and readable, borderless styling in light and dark appearances. -
#2808
291888aThanks @khoinguyenpham04! - Fixes fresh Cloudflare projects failing to startastro devwith a missingnode_modules/.vite/deps_ssrfile when Vite discoversastro/app/manifestafter startup. -
#2599
b383a67Thanks @khoinguyenpham04! - Fixes code blocks in the admin and inline visual editors so Tab and Shift+Tab indent and outdent code instead of moving focus. -
#2445
f6da16bThanks @khoinguyenpham04! - Adds a one-time, administrator-controlled process for enabling automatic media usage indexing in production. -
#2538
9c52b39Thanks @khoinguyenpham04! - Speeds up the initial scan when administrators turn on media usage tracking on Cloudflare Workers and Node.js. -
#2814
619bb56Thanks @markoinla! - Fixes reordering content types in the admin sidebar, which failed with a server error instead of saving the new order. Reordering fields within a content type was unaffected. -
#2598
0f225ebThanks @Glacier-Luo! - Fixes session-authenticated CMS comment submissions so they retain the user identity and honor automatic approval settings. -
#2600
2fde0f9Thanks @khoinguyenpham04! - Fixes the Playground welcome dialog reappearing after it has been dismissed and the page is reloaded or Edit mode is toggled. -
#2556
37c5010Thanks @cherenkov! - Fixesemdash content createandcontent updatefailing with "Cannot construct a Request with a Request object that has already been used" when the stored access token has expired. Requests carrying a body are now retried correctly after the token is refreshed. -
#2470
f527127Thanks @khoinguyenpham04! - Adds a coverage-aware Used in section to local media details. -
#2816
c3c49ddThanks @scottbuscemi! - Fixes plugin installation and activation times shifting by the server's local UTC offset when SQLite stores a zone-less timestamp. -
#2603
f5e18d8Thanks @khoinguyenpham04! - Adds one-click copy actions to code-block controls in the admin and inline visual editors, and polishes the layout so controls stay usable on narrow screens and in right-to-left locales. -
#2612
628630aThanks @danielmlr! - Fixes SQLite and D1 sites where an interrupted upgrade left the byline credits table staged as_emdash_content_bylines_new, so pages, feeds and the admin reported no entries although the content was intact. The next migration run restores the table and the stored credits. -
#2472
abd1042Thanks @khoinguyenpham04! - Fixes seeded media references using a different ID from their media-library item and prepares Used in data for newly seeded development sites. -
#2106
f00174bThanks @gruntlord5! - Fixes boolean query parameters on SQLite, including boolean equality filters in the plugin storage API. -
#2463
f613a14Thanks @helio-cf! - Fixes media previews for streaming providers such as Cloudflare Stream. Video from these providers now shows its poster thumbnail in the media library grid and list, plays in the detail panel instead of stalling at 0:00, and reports the file size the provider supplies. Also exportsMediafromemdash/ui, so frontends can render provider-backed video and audio thatImagecannot. -
Updated dependencies [
724241f,72664ad,561f1d1,3e90689,9d92b55,22c4422,2ffda17,42fa5d8,8d8d3de,70c487c,2b54096,76dd3eb,089d747,b383a67,05b0a8b,d379d10,1f2678b,724241f,436f63d,815553c,9c52b39,f527127,3367fae,f5e18d8,6178888,f9a488a,e3ad082,52f7c91,7571581,72664ad,f613a14,1c6b893]:- @emdash-cms/admin@0.36.0
- @emdash-cms/registry-client@0.4.0
- @emdash-cms/registry-lexicons@0.4.0
- @emdash-cms/registry-verification@0.2.0
- @emdash-cms/auth@0.36.0
- @emdash-cms/gutenberg-to-portable-text@0.36.0