Admin Block Editor

Replaces the Markdown editor of the admin with an Editor.js block editor. Content is still stored as Markdown: blocks are converted on save and parsed back when the editor loads.

Install

composer require pushword/admin-block-editor

Custom installations (not the default installer): see vendor/pushword/admin/install.php.

Configuration

admin_block_editor:
  new_page: true # false: new pages open in the Markdown editor (their site is not known yet)
  admin_block_editor: true # false: Markdown editor everywhere

admin_block_editor can also be set per site in the app configuration.

Add a block

  1. Write an Editor.js plugin — examples in src/assets/tools.
  2. Override @PushwordAdminBlockEditor/editorjs_widget.html.twig: extend @!PushwordAdminBlockEditor/editorjs_widget.html.twig and fill only the editorjs_block_to_add_new_plugin block.

Bundles can instead contribute tool configuration per host by implementing Pushword\AdminBlockEditor\Editor\EditorJsToolProviderInterface (the snippet and quiz bundles do); the className must name a tool already shipped in the editor bundle.

Custom tools must keep exportToMarkdown(data, tunes) deterministic and free of side effects: the editor reuses exports while a block’s type, data and tunes are unchanged.

Heading levels

The heading tool offers H2 to H6. To offer fewer, set the list after window.editorjsConfig is defined and before the editor initializes:

window.editorjsConfig.tools.header.config.levels = [2, 3, 4]

A heading already at a level the list leaves out keeps it.

Normalize undo snapshots

Custom tools can exclude transient UI data from undo history with an optional callback. Set it after window.editorjsConfig is defined and before the editor initializes:

window.editorjsConfig.undo = {
  normalizeBlockData(data, { id, type }) {
    if (type === 'customBlock') delete data.uiState
    return data
  },
}

The synchronous callback receives a deep copy of a captured block’s data and its { id, type }, and must return block data. Its result is copied into history and used to restore the block on undo or redo. Capturing leaves the live tool, id, type and tunes unchanged.

Keep the callback deterministic, idempotent and free of side effects. Unchanged blocks reuse their normalized snapshots during typing; restored data may be captured again on later edits.

History captures each block with block.save(), retaining empty and temporarily invalid blocks. A cleanup attached only to editor.saver.save() therefore does not affect history: reuse that cleanup in this callback if needed. Without a callback, history keeps the saved block data as is.

The link tool’s config (link in editorjsConfig.tools) takes three keys:

  • availableDesigns — label => class, the Style list (defaults: button, button outline, discreet);
  • availableRels — label => rel value, the Rel list (defaults: obfuscate, nofollow, nofollow sponsored, nofollow ugc);
  • options — false hides the New tab switch, Rel and Style (default: true). Editing only the address preserves an existing link’s target, rel and class.

Custom widgets can provide page titles with this Twig snippet:

window.pageLinkList = {{ editorjs_page_links(attr.page_host|default(''))|json_encode|raw }};

The older window.pagesUriList array of paths still works, without titles.

Usage

Reporting an editor problem

Open Report a problem above the editor, reproduce the issue, then download the diagnostic file and attach it to your support message. It includes the installed and loaded asset versions, browser metadata, the last 60 editing actions and up to 20 errors. Copy, history, save and tool failures also display a message in the editor. Native ResizeObserver layout warnings remain in the report as warning: resize-observer-loop and do not display an editor failure message.

The report excludes page text, field values, clipboard contents, URLs and exception messages. It stays in memory until downloaded; nothing is sent automatically.

To investigate inline predictions or rewriting, select Disable browser writing suggestions for this session, reproduce the issue again and download another report. The setting affects only the current editor session and keeps spellcheck available. Include both reports and say whether the symptom changed.

Open an Image block’s settings and choose Convert to gallery to start a gallery with that image. A gallery containing exactly one image offers Convert to image. Both conversions keep the image, edited caption, block position and settings.

Select text and open the link tool. In Page or URL, search the current site’s pages by title or path, or paste a URL. Results show titles and complete paths, with exact paths first; refine the search if more than 20 pages match.

Click a result, or use ↑/↓ and Enter to choose it. Press Enter to confirm a typed address. Escape closes the suggestions without choosing a page.

Outline panel

A collapsible left rail lists the blocks in use — in the block editor and in its Markdown/JSON source views alike. Headings nest their sections, group markers nest their content, and each row offers:

  • click to scroll to the block (highlighted on arrival),
  • a caret to fold or unfold a section,
  • drag handles — headings carry two, one for the heading alone and one for the whole section; keyboard: Alt+Arrow moves a block one row, Alt+Shift+Arrow swaps a section with the neighbouring one, bodies included,
  • delete, for the block or the whole section/group (undo restores everything).

The rail overlays the admin navigation; collapse it (state is remembered) to get the menu back — the opener stays pinned top-left.

Pages List block

Its search field takes the same syntax as the pages_list Twig function: see Page lists.