Skip to content

@emdash-cms/admin@1.2.0

Choose a tag to compare

@emdashbot emdashbot released this 06 Oct 22:10
· 19 commits to main since this release
b5f37c6

Minor Changes

  • #3056 b92f2d6 Thanks @emdashbot! - Adds an admin.locales option that limits the admin interface to the languages a site uses, which makes the admin bundle smaller.

    emdash({
    	admin: { locales: ["en", "de"] },
    });

    Only the listed languages are built into the admin and offered in its language switcher. Users whose preferred languages aren't listed see English. en is always included as the fallback, even when the list leaves it out, so admin: { locales: ["en-GB"] } still ships English alongside British English. A code the admin doesn't ship fails the build, and the error lists the available codes. Sites that don't set the option keep every language.

  • #3877 5f69896 Thanks @swissky! - Updates the admin user editor to show the name as read-only, with a hint to change it at the identity provider, when an external auth provider such as Cloudflare Access syncs names (the default). Previously the field looked editable, but the change was replaced on that user's next request. Set syncName: false in the provider config to make names editable again.

  • #3744 1e275ae Thanks @swissky! - Adds a Change domain dialog to Settings > General for moving a site to a new domain. Before it changes the Site URL, EmDash checks that the new domain serves the site. Links in emails and plugins, sitemaps, robots.txt, hreflang links, social image URLs, and canonical links set in the SEO panel then use the new domain. If the check can't reach the site, for example on localhost or behind a login, the dialog offers to store the address without the check.

    The Site URL field becomes read-only, and saving Settings > General no longer writes it. When siteUrl, EMDASH_SITE_URL, or SITE_URL is set, the page names that address, which links in emails and plugins keep using.

    Passkeys only work at the address where they were created. After a move, keep signing in at the old address, or sign in at the new one with an email link and add a passkey there.

  • #3744 1e275ae Thanks @swissky! - Adds an Email users action to Settings > General that tells every other active user where the site now lives. Each user gets an email with a button to the sign-in page at the configured siteUrl, or the Site URL when none is set, and a note that passkeys from the old address don't work there. The email does not sign anyone in. The action needs an email provider, passkey sign-in, and the users:manage permission, and shows how many emails were sent and how many the provider rejected.

    The emails come from the new POST /_emdash/api/settings/domain/notify endpoint. It accepts signed-in sessions only and can be used 3 times per hour per site.

  • #3710 06a9146 Thanks @khoinguyenpham04! - Redesigns the content editor as a page. The entry's title field is a large heading at the top, the content field's Portable Text editor sits under it without a frame, and one bar above the page holds the back link, the entry title, the save status, and Live View, Preview, and Publish. Distraction-free mode uses the same bar and adds Discard changes and Schedule.

    • Writing: Enter in the title, or Down Arrow at its end, moves into the body, and Up Arrow on the body's first line moves back. Clicking below the last block continues writing at the end. Select All selects the text of the block holding the caret, and the whole document on a second press. Backspace at the start of a heading or code block turns it into text, and next to an image, divider, or embed selects it before deleting it.
    • Toolbar: the formatting toolbar floats above the text and stays in view while you scroll, with the text fading out under it. When it doesn't fit, it scrolls sideways. Its block buttons convert blocks the same way as Turn into and the slash menu, and use the same names.
    • Blocks: hovering a block shows +, which starts a line below it with the slash menu open (Alt-click starts one above) and takes the line away again if the menu closes before anything is typed after the slash, and a handle to drag the block or open its menu: Turn into, Align, Continue numbering and Restart numbering, Duplicate, Move up and Move down, and Delete. Escape selects the block holding the caret, and the arrow keys then move between blocks. A block inserted or pasted while another is selected goes after it, and a block inserted with the caret in text goes after that block instead of splitting it. Clicking where there's no line for the caret, such as between two embeds or above an embed at the start, leaves the caret where it was; use + to add a line there.
    • Quotes and lists: quotes hold paragraphs, and list items hold text and nested lists, as Portable Text stores them. A heading, code block, image, or other block typed, pasted, or dropped into a quote or list lands beside it, where it's saved, instead of disappearing on save. A pasted quote or list that holds one is split around it, and a numbered list keeps counting after it. Alignment is offered only outside quotes and lists, where it's kept.
    • Slash menu: commands are grouped, show the Markdown that creates each block, and match abbreviated searches such as /bl for Bullet List, whole titles such as /heading 2, and the Markdown itself, such as /#. Headings 4 to 6 appear when you search for them.
    • Selection toolbar: Turn into, link, bold, italic, underline, strikethrough, and inline code, with subscript, superscript, alignment, and Clear formatting under More formatting. Clicking a link, or otherwise placing the caret in it, shows where it goes, with Edit link and Remove link, instead of selecting the whole link. A link typed as a bare domain, such as example.com, gets https://, a relative path such as ../about.html stays as typed, and a link the editor can't use says so. The selection, link, table, and image toolbars are announced as toolbars, and the Left and Right arrow keys move between their buttons.
    • Narrow screens: when the bar is narrow, as on phones and tablets, its secondary buttons, Unpublish included, show only their icons, and Publish now and Publish changes read Publish, so the bar stays on one row with the entry title readable. Block handles also show on touch screens wide enough for them.

    Other Portable Text fields keep the framed editor. PortableTextEditor adds a variant prop ("boxed" by default, or "document") and an onArrowUpAtStart callback.

  • #3827 373446f Thanks @khoinguyenpham04! - Adds a video block to the rich text editor. Type /video to choose a Media Library video or upload one. Closing the picker leaves an empty video block in place, which you can fill later by clicking it or dropping a video file on it. Video files dropped or pasted anywhere in the text also upload to the Media Library and appear where you dropped them. The video plays in the editor at the width of the text, with a caption field under it and Replace video and Delete video in its corner. A video's Used in tab in the Media Library lists the entries that use it in a video block.

    On the site, Video from emdash/ui renders the video block as the browser's own player with its caption. Media Library videos play from your storage's public URL when one is configured, and need the block's asset.url: a block without one renders nothing on the site, and the editor shows it as unplayable. A block whose asset.provider names a media provider renders from that provider's embed. An empty video block is saved without asset and renders nothing.

    Uploads follow maxUploadSize, 50 MiB by default. The admin's content security policy now allows media from blob: and https: URLs (media-src 'self' blob: https:), as it already did for images. This lets the admin read a video's size before uploading it, and preview a video block whose asset.url is on another site. Before, videos uploaded from the admin in production were saved without a width and height.

    What should I do?

    • If a plugin already defines a video block, the editor keeps using the plugin's block: it doesn't offer the built-in Video block, and dropped video files aren't uploaded. On the site, the plugin's renderer still wins; a plugin without one gets Video for blocks that have only the built-in fields. In TypeScript, narrowing PortableTextBlock on _type === "video" now gives PortableTextVideoBlock | PortableTextUnknownBlock, so reading the plugin's own fields needs a check.
    • If you edit Portable Text with portableTextToProsemirror and prosemirrorToPortableText from emdash in your own TipTap editor, add a videoBlock node with the attributes src, mediaId, provider, caption, width and height to its schema. portableTextToProsemirror turns every built-in video block into that node, and a schema without it can't load the document.
    • The media usage API and emdash/client can now return the reference type "portable_text_video" for a video used in a video block. Code that checks every reference type, or validates the list, needs to accept it.
    • With Astro's content security policy turned on and media on another host, allow that host in media-src.
  • #3773 e6fe5d4 Thanks @khoinguyenpham04! - Adds numbered pages to every collection list in the admin. The All and Trash tabs load one page of entries at a time, so a collection opens with 20 entries instead of fetching 100, and both tabs use the Media Library's pagination footer pinned to the bottom of the screen: the entry range, 20, 50, or 100 entries per page, and controls to jump to any page. The Trash badge counts every trashed entry instead of stopping at 50, and every trashed entry is reachable.

    Selections persist across pages. Changing the search, a filter, the locale, or the collection clears them.

    GET /_emdash/api/content/{collection} and GET /_emdash/api/content/{collection}/trash accept a 1-based page parameter instead of cursor. A numbered page returns total and no nextCursor, and sending both page and cursor returns a 400 validation error. Cursor pagination is unchanged.

    ContentList accepts optional pagination and trashPagination props for numbered pages; without them it behaves as before.

  • #3744 1e275ae Thanks @swissky! - Adds a Continue on button to Settings > General after a site moves to a new domain. Passkeys only work at the address where they were created, so a user signed in at the old address can select the button to sign in at the new one without email. The single-use link expires after 5 minutes and opens Settings > Security, ready to add a passkey for the new address. The button appears when you are signed in at an address other than the Site URL, or the configured siteUrl when one is set. It is not shown when an external provider such as Cloudflare Access handles sign-in.

    The link comes from the new POST /_emdash/api/auth/handover endpoint, which accepts signed-in sessions only and allows 5 links per user every 5 minutes. GET /_emdash/api/settings/domain now also returns siteOrigin, the address the link points to.

    @emdash-cms/auth exports createMagicLinkUrl(), which creates a single-use sign-in link without sending an email.

Patch Changes

  • #3878 5c1ebc3 Thanks @swissky! - Fixes a blank screen when an entry in the admin content editor fails to open. A missing entry now shows a not-found page, and a subscriber opening an unpublished entry is told that their role can only view published content. Other errors show the error message with a retry button.

  • #3798 90f39e0 Thanks @DiogoDuart3! - Fixes long block type descriptions in the "Add block" picker of a blocks field running into the next column. Each option now stays within its column and its description wraps.

  • #3925 a17408f Thanks @danielmlr! - Fixes the byline editor not scrolling when its fields don't fit in the window, which hid the lower fields, such as the linked user, custom fields and translations, behind the dialog's buttons. The form now scrolls, and the title and buttons stay in place.

  • #3852 f5406f8 Thanks @miljan-aleksic! - Completes the Catalan admin translations so every admin string is localized, keeping the source's punctuation, ICU placeholders, and rich-text tags verbatim, leaving "Token" and technical terms untranslated, and using exemple.com for example domains.

  • #3831 3787eb9 Thanks @emdashbot! - Fixes false conflict errors when saving SEO fields, including OG images, after a clean content editor refreshes a cached entry. Background refreshes preserve unsaved edits and their conflict protection, and delayed older reads cannot replace the revision from a successful save.

  • #3728 e63cc44 Thanks @huketo! - Keeps the WordPress migration-key and site-URL fields inside their cards on narrow screens, while preserving the wider-screen layout. Technical keys and URLs remain left-to-right in RTL locales, and the site-URL input exposes its localized name to assistive technologies.

  • #2791 c77c6cf Thanks @SL33PiNg! - Completes the Thai (th) admin translation. Thai admins now see localized text across the publishing calendar, editor image toolbar, site settings, authentication, plugin registry and capability consent dialogs, site transfer, and the media library, which previously fell back to English for 1,725 of 3,188 strings.

  • #3558 395087d Thanks @kgni! - Translates the new admin strings into Danish, including the Bylines page, the bulk tag dialog, sections, menus and the content type editor.

  • #3744 1e275ae Thanks @swissky! - Fixes email links pointing to the address a site was set up on after it moved to a new domain. Sign-in, invitation, self-signup, recovery, and comment notification emails now use the Site URL from Settings > General when siteUrl, EMDASH_SITE_URL, or SITE_URL is not configured, and fall back to the setup address when the field is empty. Only the origin of the Site URL is used, and it must use https:// unless the host is a loopback address. A configured siteUrl still takes precedence. emdash export-seed no longer copies the Site URL into the seed.

    If you don't configure siteUrl and the Site URL field holds an address that doesn't serve this site's admin, for example an old domain, links in these emails point there after upgrading. Check the field before upgrading; clearing it restores the previous behavior. Seeds that set settings.url, including seeds exported by earlier versions, still fill in the Site URL, so remove url from a seed copied from another site before using it.

  • #3721 24a4327 Thanks @miljan-aleksic! - Updates Spanish (Spain) admin translations to add missing strings, remove deprecated ones, correct wrong and inconsistent translations, and switch to the informal "tú" tone.

  • #3796 9f09f60 Thanks @DiogoDuart3! - Adds European Portuguese (Português (Portugal), pt-PT) translations for the admin UI. The locale is selectable from the language picker and uses Portuguese date formats in the calendar and date settings. Browsers that ask for pt-PT now get this locale instead of Brazilian Portuguese, and a site whose language is set to pt-PT sends its invite, sign-in and recovery emails in European Portuguese. Other Portuguese variants, and plain pt, still resolve to Brazilian Portuguese.

  • #3701 8885267 Thanks @ShaneMuir! - Fixes the field editor silently closing and discarding changes when the server rejects a field update, such as toggling required or unique on a field that already has content (which needs a manual content migration). The dialog now stays open and shows the server's error message instead of the toggle looking like it reverted on its own.

  • #3813 c07437f Thanks @emdashbot! - Fixes generated admin identifier slugs for non-ASCII labels in content types, taxonomies, byline fields, relations, and fields.

    Labels like Größe now produce groesse and Título becomes titulo, including equivalent decomposed Unicode input. Labels that cannot produce a valid ASCII identifier, such as 名前 or 2024年, require a manually entered slug and show an inline message.

    Adds editable slugs for new repeater sub-fields and blocks duplicate sub-field slugs before saving. Existing sub-field slugs stay read-only so relabeling preserves the keys used by saved content.

  • #3835 b3e17f6 Thanks @danielmlr! - Completes the German admin translations so German-speaking editors see localized text for the publishing calendar, domain settings, HTML and iframe blocks, media controls, visual editing, and plugin permissions.

  • #3293 595fd15 Thanks @itaides! - Adds Hebrew (עברית) to the admin language picker. The admin renders right-to-left in Hebrew, its date pickers use Hebrew month and weekday names, and its strings are translated as of October 2026. Strings added to the admin later appear in English until the catalog is updated.

  • #3872 392ade3 Thanks @Zahid09987! - Adds Indonesian translations for recently introduced admin UI strings, including slug-generation hints, the sub-field slug placeholder, and sub-field validation messages.

  • #3851 3bcd2cb Thanks @Zahid09987! - Adds Indonesian translations for the site domain-change flow, general settings screen, marketplace capability labels, and visual-editing toolbar strings.

  • #3795 36aee2d Thanks @DiogoDuart3! - Fixes the content editor failing to open any entry with a date field when the site timezone setting is not a valid IANA timezone (for example Lisboa instead of Europe/Lisbon). The editor now falls back to UTC for such a value instead of crashing, and the settings API and MCP settings tool reject an unrecognized timezone with a validation error. A site that already stores one can still save its other settings, and can fix the timezone in Settings > General.

  • #3931 9538600 Thanks @danielmlr! - Fixes the error message on the invite and signup pages ending in a bare colon when the server responds with an error page instead of an error message. The message no longer includes the HTTP status text, which browsers leave empty over HTTP/2 and HTTP/3, and it can now be translated.

  • #3789 6d2a3bb Thanks @maikunari! - Aligns the Japanese admin text with the long-vowel spelling used elsewhere in the catalog: ブラウザー and エディター now replace ブラウザ and エディタ in the passkey messages, the rich text editor label, and the WordPress import notice.

  • #3718 841a5b3 Thanks @huketo! - Expands the Korean admin translations to cover almost every admin screen and corrects terminology in the existing Korean text. Korean remains disabled in the locale selector.

    The catalog now covers sign-in, passkeys, invitation and sign-in emails, and device authorization; the dashboard, setup, and shared notifications, error, and loading screens; content lists, editing, publishing, and scheduling; rich-text, image, gallery, and block editing; content types, fields, relationships, and references; media; bylines and taxonomies; menus, widgets, and sections; site settings, users, redirects, backups, and transfer tokens; plugins, themes, and the registry; and site transfer and WordPress imports.

    The Korean text distinguishes content authors from public byline profiles, and the permanent-delete confirmation now shows the content title instead of leaving it blank.

  • #3732 2210c2c Thanks @ascorbic! - Fixes plugin permissions for sending email, handling outgoing email, and adding page scripts and styles showing as raw capability names in the admin. They now have readable labels in the plugin list, marketplace, and install consent dialog.

  • #3923 142da40 Thanks @ascorbic! - Reduces the admin's JavaScript by about 220 KB by loading the date picker and date-format localization for the active language on demand, instead of bundling every language.

  • #3933 d72b615 Thanks @danielmlr! - Fixes admin error messages for menus, taxonomies, widgets, users, and other screens that stayed in English when the server answered with an error page instead of an error message, such as a gateway error. They now follow the admin language.

  • #3010 2f59cc3 Thanks @eisenbruch! - Fixes the content list showing a "Pending changes" badge on entries that have never been published. A draft or scheduled entry has a draft revision and no live revision, which the list read as a difference between the two. The badge now appears only where there is a published version for the changes to be pending against, matching the state the editor shows for the same entry.

  • #3773 e6fe5d4 Thanks @khoinguyenpham04! - Fixes keyboard focus in the Media Library pagination controls. Focus no longer jumps to those controls when the library reloads after a search or filter change; it returns to them only after a page they requested finishes loading, and stays wherever you moved it during the load. Picking a page or a page size from a dropdown returns focus to that dropdown, and when the page reached disables the button you pressed, such as Previous on the first page, focus moves to the page picker instead of being lost.

  • #3710 06a9146 Thanks @khoinguyenpham04! - Fixes newly inserted HTML, iframe, and code blocks in the content editor. HTML and iframe blocks now highlight the selected tab, and their arrow keys move between tabs. Tab now reaches a code block's language and copy buttons, and the arrow keys move between them. Until now, these worked only for blocks that were there when the page loaded.

  • #3758 d0c7384 Thanks @danielmlr! - Fixes the admin's new-entry form ignoring field default values: a boolean field with defaultValue: true started switched off, and saving the entry untouched could store no value. New entries in the admin now start with each field's default value.

    Manifest field descriptors now include stored field defaults as defaultValue, except for relation-bound reference fields.

  • #3369 b854616 Thanks @danielmlr! - Fixes plugin admin pages and dashboard widgets showing no feedback after a Block Kit button click or other action. Until the plugin responds, the content now dims under a loading indicator, and screen readers announce that it is updating.

  • #3546 2c8c12a Thanks @swissky! - Adds group to plugin admin pages, in native plugin descriptors and in admin.pages of emdash-plugin.jsonc, to place them in collapsible admin sidebar folders. A page whose group matches the group of a collection shown in the sidebar appears inside that folder, after its collections and taxonomies. Pages that share any other group, from one plugin or several, fold into one folder in the Plugins section. Pages without a group stay where they are.

  • #3737 8867aba Thanks @ryofukutani! - Fixes plugin block cards in the content editor dropping number_input values from their summary line, so a block whose only identifying field is a number (such as an episode or video number) no longer shows just the block type.

  • #1899 e9cf2c3 Thanks @swissky! - Adds an optional urlTemplate prop to the core:recent-posts widget (e.g. "/blog/:slug" or "/:slug" for catch-all routes), using the same :collection, :id, :slug, and :path tokens as LiveSearch's routeMap, with a localized label in the admin widget form. Without a template the widget links exactly as before.

  • #3559 f6ee57e Thanks @khoinguyenpham04! - Fixes Select All inside code blocks in the admin and inline visual editors so it selects only the code instead of the entire document.

  • #3810 788a371 Thanks @miljan-aleksic! - Updates the Serbian (Latin) admin translations to address the user informally ("ti") throughout. Singular imperatives and second-person wording replace the previous mix of formal and plural forms, passive constructions become active, and redundant possessive pronouns are dropped. A mixed-script character, a misspelling, and a few English calques are also corrected.

  • #3771 550e59b Thanks @danielmlr! - Fixes the visual editing toolbar's Publish button switching to English after a save when the toolbar is shown in another language. The button now keeps its translated label, and the toolbar's status badges and image popover can be translated as well.

  • #3786 8037f5a Thanks @emdashbot! - Fixes plugin dashboard widget titles so they translate through the same shared Lingui catalog as admin page labels. Widgets without a title still fall back to the raw widget id.

  • #3751 dea8f58 Thanks @danielmlr! - Fixes the WordPress import's media step stopping partway with "No result received from media import" on Cloudflare Workers Paid, where a batch of large photos could exceed the default 30-second CPU limit. The admin now imports media in smaller batches to stay within that limit.

  • #3860 c5cef43 Thanks @leevincent! - Completes the Traditional Chinese (Taiwan, zh-TW) admin translations so editors see localized text in the publishing calendar, media library, site transfer tools, plugin permission dialogs, and visual editing toolbar.

  • Updated dependencies [2c8c12a, 2210c2c]:

    • @emdash-cms/plugin-types@0.6.0
    • @emdash-cms/blocks@1.2.0