Add configurable code blocks and inline code - #1193
Conversation
Adds `codeBlocks` and `inlineCode` options (both default `true`), configurable separately via preset or element attribute, following the same shape as the existing `richText` / `markdown` / `attachments` options. When disabled: - Insertion is removed from the toolbar and Markdown. The shared code button routes to whichever feature is still enabled and is hidden only when both are off. - Existing code blocks and inline code are reduced to plain text on load and paste via node transforms. The code nodes stay registered so the highlight, format-escape, and Trix conversion extensions keep working. Documented in home/docs/configuration.md. Covered by JS unit, Playwright, and Capybara round-trip tests.
There was a problem hiding this comment.
Pull request overview
Adds per-editor and preset-level configuration switches for code blocks and inline code in Lexxy, preserving current defaults while allowing apps to disable either feature (including load/paste stripping and Markdown shortcut suppression).
Changes:
- Add
codeBlocksandinlineCodeconfiguration options (defaulttrue) with element-attribute overrides. - Adjust code insertion routing, Markdown transformers, and import/paste/load behavior to strip disabled code formats.
- Add unit, Playwright, and system tests plus fixtures/docs/CSS updates for the new options.
Tip
If you aren't ready for review, convert to a draft PR.
Click "Convert to draft" or run gh pr ready --undo.
Click "Ready for review" or run gh pr ready to reengage.
Reviewed changes
Copilot reviewed 12 out of 12 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| src/elements/editor.js | Adds support flags, transformer filtering, and stripping transforms; passes feature state to toolbar via data attributes. |
| src/editor/command_dispatcher.js | Routes the shared “code” action based on which code features are enabled. |
| src/config/lexxy.js | Introduces default preset values for codeBlocks/inlineCode. |
| app/assets/stylesheets/lexxy-editor.css | Hides the shared code toolbar button only when both features are disabled. |
| home/docs/configuration.md | Documents codeBlocks and inlineCode options and behavior when disabled. |
| test/javascript/unit/editor/code_configuration.test.js | Unit tests for config resolution (default/preset/attribute overrides). |
| test/browser/tests/formatting/code_disabled.test.js | Playwright coverage for toolbar visibility/routing, Markdown suppression, and load stripping. |
| test/browser/fixtures/code-blocks-false.html | Fixture for code-blocks="false" state. |
| test/browser/fixtures/inline-code-false.html | Fixture for inline-code="false" state. |
| test/browser/fixtures/code-all-false.html | Fixture for both code features disabled. |
| test/system/code_disabled_test.rb | System test for Action Text round-trip stripping (load → save → render → re-edit). |
| test/dummy/app/views/posts/_form.html.erb | Adds query-param driven attributes to exercise disabled states in the dummy app. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| #createDefaultToolbar() { | ||
| const toolbar = createElement("lexxy-toolbar") | ||
| toolbar.innerHTML = LexicalToolbar.defaultTemplate | ||
| toolbar.setAttribute("data-attachments", this.supportsAttachments) // Drives toolbar CSS styles | ||
| toolbar.setAttribute("data-code-blocks", this.supportsCodeBlocks) | ||
| toolbar.setAttribute("data-inline-code", this.supportsInlineCode) | ||
| toolbar.configure(this.config.get("toolbar")) |
There was a problem hiding this comment.
Good catch, and technically correct. But this is a pre-existing limitation, not one introduced here: data-attachments (set on the same line, #createDefaultToolbar) has the exact same behaviour. It's only applied to toolbars Lexxy creates, never to external toolbars referenced by ID. The data-code-blocks/data-inline-code attributes follow that established pattern deliberately, so the PR stays internally consistent.
Fixing external-toolbar support here would mean moving all three attributes into #attachToolbar, which (a) expands this PR's scope to the unrelated attachments feature, and (b) makes Lexxy start writing attributes onto a user-owned element. A multi-editor setup gets more complicated at that point. I'd argue it's a separate, deliberate change and out of scope here.
| await expect.poll(() => editor.value()).toContain("<pre") | ||
| expect(await editor.value()).not.toContain("<code") | ||
| }) |
| // Inline code is a text format rather than a node, so strip the format from | ||
| // any imported or pasted content. Runs on load and paste; a no-op for | ||
| // unformatted text. |
| - `codeBlocks`: Pass `false` to disable code blocks. Insertion via the toolbar and Markdown is removed, and any existing `<pre>` code is reduced to plain text when loaded. By default, code blocks are enabled. | ||
| - `inlineCode`: Pass `false` to disable inline `code`. Insertion via the toolbar and Markdown is removed, and any existing inline `<code>` is reduced to plain text when loaded. By default, inline code is enabled. |
| // Inline code is a text format rather than a node, so strip the format from | ||
| // any imported or pasted content. Runs on load and paste; a no-op for | ||
| // unformatted text. |
| paragraphs.forEach((paragraph) => node.insertBefore(paragraph)) | ||
| node.remove() | ||
| }) |
There was a problem hiding this comment.
I think editing another tool would be out of scope of this PR
|
@jorgemanrubia I saw your recent tweet on making Lexxy configurable and extensible. I totally I agree. Can you please take a look at this PR and let me know if this is along the lines you're looking for? |
Adds
codeBlocksandinlineCodeoptions toLexxy.configure. They are configurable separately via preset or element attribute. Both defaulttrue, default behaviour is unchanged.Lexxy's configuration and is breadth of formatting options is great, however, I have some applications where I don't need or can't use features such as code blocks. This allows for optionally disabling code them.
This follows the API and structure laid out in #1169 (configurable headers) and the existing
richText/markdown/attachmentsoptions. It uses the sameLexxy.configureboolean-option shape, per-editor element attributes, data-* + CSS toolbar hiding, and load-time stripping to plain text. This extends that established pattern to code blocks and inline code. It solves part of #1097.What
Adds two options (both default
true), configurable separately via preset or element attribute:How it works when disabled
Code blocks and inline code share one toolbar button (
name="code"), which routes to inline code when text is selected on a single line and to a code block otherwise. Making them independently disablable splits that routing rather than just hiding the button:data-code-blocks/data-inline-codecompound CSS rule) only when both are off. TheCODE/INLINE_CODEMarkdown transformers are filtered out per feature.CodeNodetransform rewrites code blocks to paragraphs, and aTextNodetransform clears the inlinecodeformat. The code nodes stay registered because the highlight, format-escape, and Trix-conversion extensions register transforms against them for every rich-text editor. Unregistering them breaks bootstrap.Testing
test/javascript/unit/editor/code_configuration.test.js) config resolution across default / preset / attribute override, mirroring the headings config test.test/browser/tests/formatting/code_disabled.test.js) button visibility, routing, Markdown suppression, and load-stripping for each of the three states (code blocks off, inline off, both off).test/system/code_disabled_test.rb) Action Text round-trip (load → strip → save → render → re-edit) for both features.Docs updated to match (
home/docs/configuration.md).