agentsclimarketplace

Wp admin codemirror

Skill Lonsdale201/wp-agent-skills/wordpress/wp-admin-codemirror

Embed WordPress's bundled CodeMirror editor in admin pages via `wp_enqueue_code_editor()` and `wp.codeEditor.initialize()`. Covers MIME / file mode selection for CSS, JS, JSON, HTML, PHP, SQL, Markdown, and YAML; the `false` return when user profile syntax highlighting is disabled; passing settings to JS; the bare textarea ID requirement for `initialize( 'mytextarea', settings )`; reading values with `instance.codemirror.getValue()`; `wp_code_editor_settings`; and linter handles such as `csslint`, `htmlhint`, `htmlhint-kses`, and `jsonlint`. Use for custom CSS, snippets, JSON schemas, webhook previews, regex fields, or any plugin settings textarea that needs syntax highlighting.From its SKILL.md

Install
npx -y skills add Lonsdale201/wp-agent-skills --skill wp-admin-codemirror

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 21 stars21 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

SKILL.md

13.6 KB, ~3.2k tokens by cl100k_base, as published. Nobody here has run it

WordPress Admin CodeMirror (wp.codeEditor)

WP bundles CodeMirror 5 plus its linter add-ons. Plugins almost never use it because the entry point is one underused function (wp_enqueue_code_editor) and the JS init is split between two files. This skill is the bootstrap recipe.

When to use this skill

Trigger when ANY of the following is true:

  • The user is adding a <textarea> to admin for CSS / JSON / JS / HTML / PHP / SQL / regex / Markdown / YAML content and wants syntax highlighting (NOT the block editor).
  • Code references wp_enqueue_code_editor, wp_get_code_editor_settings, wp.codeEditor, wp.CodeMirror, wp_code_editor_settings, code-editor script handle.
  • The user is building a "custom CSS field", "snippet editor", "JSON config editor", "webhook payload field", "regex builder", "shortcode preview", "rules engine".
  • The user mentions CodeMirror, Monaco, Ace, Prism specifically in a WP-admin context — and the answer is "use the bundled CodeMirror".

The five-step bootstrap

add_action( 'admin_enqueue_scripts', static function ( string $hook_suffix ): void {
    // 1. Only on your screen.
    if ( 'settings_page_myplugin' !== $hook_suffix ) {
        return;
    }

    // 2. Ask WP to enqueue CodeMirror and bundle a settings snapshot for this MIME type.
    //    Use 'type' (MIME) or 'file' (extension sniff).
    $settings = wp_enqueue_code_editor( array( 'type' => 'text/css' ) );

    // 3. CRITICAL: $settings is false when the user has disabled syntax highlighting
    //    in their profile. Stop here; the textarea stays as a plain textarea.
    if ( false === $settings ) {
        return;
    }

    // 4. Enqueue YOUR admin script that calls wp.codeEditor.initialize().
    wp_enqueue_script(
        'myplugin-css-editor',
        plugins_url( 'assets/css-editor.js', MYPLUGIN_FILE ),
        array( 'code-editor', 'wp-i18n' ),
        MYPLUGIN_VERSION,
        array( 'in_footer' => true )
    );

    // 5. Pass the textarea ID and per-instance settings to your script.
    wp_add_inline_script(
        'myplugin-css-editor',
        'window.MyPluginCssEditor = ' . wp_json_encode( array(
            'id'       => 'myplugin_custom_css',
            'settings' => $settings,
        ) ) . ';',
        'before'
    );
} );
// assets/css-editor.js
jQuery( function () {
    if ( ! window.wp || ! wp.codeEditor || ! window.MyPluginCssEditor ) {
        return;
    }
    wp.codeEditor.initialize(
        MyPluginCssEditor.id,
        MyPluginCssEditor.settings
    );
} );

When the first argument is a string, pass the bare textarea id with no #. If you want to pass a CSS selector, pass a jQuery object instead: wp.codeEditor.initialize( jQuery( '#myplugin_custom_css' ), settings ).

// In your settings view, the underlying field stays a plain <textarea>.
// Submit-time the form posts the textarea value as if CodeMirror weren't there.
?>
<textarea id="myplugin_custom_css" name="myplugin_options[custom_css]" rows="10" cols="60"><?php
    echo esc_textarea( $options['custom_css'] ?? '' );
?></textarea>

That's the whole flow for a native form submission. CodeMirror's EditorFromTextArea saves its value back into the original <textarea> when that form submits.

Reading / writing programmatically

wp.codeEditor.initialize() returns an object with .codemirror (the underlying CodeMirror 5 instance with full API):

const editor = wp.codeEditor.initialize( 'myplugin_custom_css' );

editor.codemirror.getValue();                          // current text
editor.codemirror.setValue( '/* new content */' );     // replace text
editor.codemirror.on( 'change', () => { /* ... */ } ); // change events
editor.codemirror.focus();
editor.codemirror.refresh();                           // call after un-hiding (tabs, accordion)
editor.updateErrorNotice();                            // force-refresh the linter notice

The .refresh() call is non-obvious and important — if your editor lives inside a hidden tab, accordion, or modal that's shown later, the editor renders at width=0 until you refresh() after it becomes visible.

Picking the right type / file

wp_enqueue_code_editor() accepts either type (a MIME string) or file (a filename — extension is sniffed). The full per-MIME mode mapping lives in wp_get_code_editor_settings() (wp-includes/general-template.php:4128). Common values:

Use caseRecommended call
CSSarray( 'type' => 'text/css' )
JavaScriptarray( 'type' => 'text/javascript' )
JSONarray( 'type' => 'application/json' )
HTMLarray( 'type' => 'text/html' )
PHParray( 'type' => 'application/x-httpd-php' )
SQLarray( 'type' => 'text/x-sql' )
Markdownarray( 'type' => 'text/x-markdown' )
YAMLarray( 'type' => 'text/x-yaml' )
Filename sniffarray( 'file' => 'config.json' )

Linter handles WP auto-enqueues

Based on the MIME and the final codemirror.lint setting, wp_enqueue_code_editor() registers the relevant linter (wp-includes/general-template.php:4053-4090):

MIME / settingLinter handles enqueued
CSS / SCSS / LESS with WP defaultsNone — these modes default to lint: false
CSS / SCSS / LESS after you explicitly set codemirror.lint: truecsslint
HTML with default lint: truehtmlhint, csslint, plus htmlhint-kses if the user lacks unfiltered_html
PHP with WP defaultsNone — PHP mode defaults to lint: false
PHP after you explicitly set codemirror.lint: truehtmlhint, csslint, plus htmlhint-kses if the user lacks unfiltered_html
JavaScript / JSON with default lint: truejsonlint; JS lint settings also include the Espree module URL in current WP

You don't enqueue these — listing them above is so you can predict what shows up in DevTools and so you know what's missing if you replace the default settings.

Disabling the linter for a specific instance

Pass lint: false in the codemirror settings to suppress the gutter and the "X errors" notice:

wp.codeEditor.initialize( 'read-only-preview', {
    codemirror: { readOnly: true, lint: false },
} );

Customizing defaults globally — wp_code_editor_settings filter

When you want to change CodeMirror defaults for everyone (e.g. force 2-space indent, add a custom mode, disable a CSSLint rule for the whole admin), filter wp_code_editor_settings:

add_filter( 'wp_code_editor_settings', static function ( array $settings, array $args ): array {
    if ( ( $args['type'] ?? '' ) !== 'text/css' ) {
        return $settings;
    }

    // Override CodeMirror options.
    $settings['codemirror']['indentUnit']     = 2;
    $settings['codemirror']['indentWithTabs'] = false;

    // Disable a specific CSSLint rule.
    $settings['csslint']['box-model'] = false;

    return $settings;
}, 10, 2 );

Defaults you'll usually want to keep (set in wp_get_code_editor_settings() itself, line 4128): lineNumbers: true, lineWrapping: true, styleActiveLine: true, continueComments: true, plus shortcuts Ctrl-Space autocomplete, Ctrl-/ toggleComment, Ctrl-F findPersistent.

The two callbacks worth knowing

wp.codeEditor.initialize( 'my-editor', {
    onChangeLintingErrors: function ( errorAnnotations, allAnnotations ) {
        // Disable the form's Save button while there are lint errors.
        const $save = jQuery( '#submit' );
        $save.prop( 'disabled', errorAnnotations.length > 0 );
    },
    onUpdateErrorNotice: function ( errorAnnotations, codemirror ) {
        // Custom UI for the error notice. WP shows its own by default.
    },
} );

onChangeLintingErrors is the right hook for "block save until valid" UX. Don't poll .codemirror.state from a setInterval — that's the AI-default but wrong pattern.

Critical rules

  • Always guard against wp_enqueue_code_editor() returning false. If the user has disabled syntax highlighting in their profile (Users → Your Profile → Syntax Highlighting), the call returns false and does NOT enqueue. Your follow-up wp_enqueue_script( 'my-code-editor' ) would still load and throw wp.codeEditor is not defined. Defensive guard:

    $settings = wp_enqueue_code_editor( array( 'type' => 'text/css' ) );
    if ( false === $settings ) {
        return; // Leave the plain <textarea> alone.
    }
    

    And in JS:

    if ( ! window.wp || ! wp.codeEditor ) {
        return;
    }
    
  • Call initialize() inside DOMContentLoaded. The function itself logs console.warn('wp.codeEditor.initialize() ran too early...') when called pre-DOMContentLoaded (wp-admin/js/code-editor.js:417). Wrap in jQuery( function () { ... } ) or document.addEventListener( 'DOMContentLoaded', ... ).

  • Don't wp_enqueue_code_editor() globally. It runs on every admin page-load and adds ~6 scripts + 1 stylesheet plus linters. Always gate on $hook_suffix.

  • The <textarea> must exist before initialize() runs. CodeMirror replaces the textarea in the DOM — late-added textareas (e.g. in a repeater) need their own initialize() after insertion.

  • Don't re-initialize an already-initialized textarea. Track instances; if you must re-init, call instance.codemirror.toTextArea() first to detach.

  • Native form submit is synchronized; arbitrary serialization is not. EditorFromTextArea updates the original <textarea> for the form's native submit event. Before programmatically reading or serializing that textarea, call instance.codemirror.save() or use getValue() directly. It does not write the textarea on every editor change.

Common mistakes

// WRONG — assumes wp.codeEditor exists; throws when user disabled highlighting in profile
wp.codeEditor.initialize( 'snippet' );

// RIGHT — guard
if ( window.wp && wp.codeEditor ) {
    wp.codeEditor.initialize( 'snippet' );
}
// WRONG — runs on every admin page
add_action( 'admin_enqueue_scripts', 'my_plugin_load_code_editor' );
function my_plugin_load_code_editor(): void {
    wp_enqueue_code_editor( array( 'type' => 'text/css' ) );
}

// RIGHT — gate on the screen
add_action( 'admin_enqueue_scripts', static function ( string $hook_suffix ): void {
    if ( 'settings_page_myplugin' !== $hook_suffix ) {
        return;
    }
    wp_enqueue_code_editor( array( 'type' => 'text/css' ) );
    // ...
} );
// WRONG — editor inside hidden tab renders at width=0; user sees nothing
$tab.show();

// RIGHT — refresh after show
$tab.show();
instance.codemirror.refresh();
// WRONG — using PHPCS / phpcbf style ruleset; CodeMirror uses CSSLint / JSHint / HTMLHint
$settings['codemirror']['phpcs'] = array( 'WordPress' );

// RIGHT — those linters don't exist client-side. Disable the lint if you only want highlighting.
$settings['codemirror']['lint'] = false;

Cross-references

  • See wp-plugin-assets-loading for the canonical $hook_suffix gate pattern.
  • See wp-admin-form-controls for the sibling form controls (color picker, date picker, pointer) that often sit on the same settings page as a code field.
  • See wp-admin-settings-api when the code field is part of a register_setting() options page; the sanitize_callback runs on the textarea value, which CodeMirror has already synced.

What this skill does NOT cover

  • The Customizer / block editor code-editing surfaces. Those use the same wp.codeEditor but the host integration differs.
  • Replacing CodeMirror with Monaco or Ace. Out of scope — use the bundled one.
  • Custom CodeMirror modes not bundled in core. WP ships CSS, JS, JSON, HTML mixed, PHP (clike), XML, Markdown, YAML, SQL, Stylus, SCSS, LESS, JSX. Anything else, you bundle yourself.
  • Server-side validation of the submitted code. That's a sanitize_callback / wp_kses topic, not a CodeMirror topic.

References

What ships with it

Read from the repository

Just SKILL.md. No reference files, no scripts.

Keep looking

Skills are one crate of 325,949. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.