{"id":365047,"date":"2026-09-11T15:02:52","date_gmt":"2026-09-11T15:02:52","guid":{"rendered":"https:\/\/wordpress.org\/plugins\/editor-tool-rail\/"},"modified":"2026-09-11T15:02:27","modified_gmt":"2026-09-11T15:02:27","slug":"editrail","status":"publish","type":"plugin","link":"https:\/\/wordpress.org\/plugins\/editrail\/","author":23409562,"comment_status":"closed","ping_status":"closed","template":"","meta":{"version":"1.0.1","stable_tag":"1.0.1","tested":"7.1","requires":"6.5","requires_php":"7.4","requires_plugins":null,"header_name":"EditRail","header_author":"Matthew Cowan","header_description":"A movable, graphics-editor-style toolbar for the block editor. Click a tool, then click the canvas to insert a core block at that point.","assets_banners_color":"1e1e1e","last_updated":"2026-09-11 15:02:27","external_support_url":"","external_repository_url":"","donate_link":"","header_plugin_uri":"","header_author_uri":"https:\/\/mnc4.com\/","rating":0,"author_block_rating":0,"active_installs":0,"downloads":39,"num_ratings":0,"support_threads":0,"support_threads_resolved":0,"author_block_count":0,"sections":["description","faq","changelog"],"tags":{"1.0.1":{"tag":"1.0.1","author":"matthewneilcowan","date":"2026-09-11 15:02:27","revision":3691627}},"upgrade_notice":{"1.0.1":"<p>The plugin folder is now editrail. After a zip upgrade, delete the old toolrail folder so two copies do not load. After a git deploy, activate EditRail again once in the Plugins screen. Pinned tools and saved sets are kept.<\/p>"},"ratings":[],"assets_icons":{"icon-128x128.png":{"filename":"icon-128x128.png","revision":3691627,"resolution":"128x128","location":"assets","locale":"","width":128,"height":128},"icon-256x256.png":{"filename":"icon-256x256.png","revision":3691627,"resolution":"256x256","location":"assets","locale":"","width":256,"height":256},"icon.svg":{"filename":"icon.svg","revision":3691627,"resolution":false,"location":"assets","locale":false}},"assets_banners":{"banner-1544x500.png":{"filename":"banner-1544x500.png","revision":3691627,"resolution":"1544x500","location":"assets","locale":"","width":1544,"height":500},"banner-772x250.png":{"filename":"banner-772x250.png","revision":3691627,"resolution":"772x250","location":"assets","locale":"","width":772,"height":250}},"assets_blueprints":{"blueprint.json":{"filename":"blueprint.json","revision":3691627,"resolution":false,"location":"assets","locale":"","contents":"{\"$schema\":\"https:\\\/\\\/playground.wordpress.net\\\/blueprint-schema.json\",\"landingPage\":\"\\\/wp-admin\\\/post.php?post=2026&action=edit\",\"preferredVersions\":{\"php\":\"8.2\",\"wp\":\"latest\"},\"steps\":[{\"step\":\"installTheme\",\"themeData\":{\"resource\":\"wordpress.org\\\/themes\",\"slug\":\"twentytwentyfive\"},\"options\":{\"activate\":true}},{\"step\":\"installPlugin\",\"pluginData\":{\"resource\":\"wordpress.org\\\/plugins\",\"slug\":\"typography-stylist\"},\"options\":{\"activate\":true}},{\"step\":\"installPlugin\",\"pluginData\":{\"resource\":\"wordpress.org\\\/plugins\",\"slug\":\"editrail\"},\"options\":{\"activate\":true}},{\"step\":\"mkdir\",\"path\":\"\\\/wordpress\\\/wp-content\\\/mu-plugins\"},{\"step\":\"writeFile\",\"path\":\"\\\/wordpress\\\/wp-content\\\/mu-plugins\\\/toolrail-demo-fonts.php\",\"data\":\"<?php\\n\\\/**\\n * Plugin Name: EditRail demo fonts\\n * Description: Registers EB Garamond in the WordPress Font Library for the Playground demo post, from the font's own repository. Not part of the plugin.\\n *\\n * Written into \\\/wordpress\\\/wp-content\\\/mu-plugins\\\/ by the Playground\\n * blueprint (scripts\\\/playground.js embeds this file).\\n *\\n * EB Garamond (SIL Open Font License 1.1, the EB Garamond Project\\n * Authors, https:\\\/\\\/github.com\\\/octaviopardo\\\/EBGaramond12) is served from\\n * the google\\\/fonts repository, which sends Access-Control-Allow-Origin: *\\n * so the browser can load it cross-origin. These are the full variable\\n * files, not the Google Fonts CSS service, which strips OpenType\\n * features the demo depends on (swsh, dlig, ss01).\\n *\\n * Two registrations, on purpose:\\n *\\n * 1. theme.json (settings.typography.fontFamilies). This is what puts\\n *    the font in the WordPress Font Library, and the Library is where\\n *    Typography Stylist's font picker reads fonts from. The demo's\\n *    runPHP step then adopts the font through the plugin's own bridge,\\n *    so it has a font_id and the demo post's blocks show it as the\\n *    selected font. WordPress also prints @font-face for the faces\\n *    declared here, on the front end and in the editor.\\n * 2. An inline @font-face on enqueue_block_assets, the same two faces.\\n *    A belt for the braces: it reaches the editor's canvas iframe and\\n *    the front end whatever WordPress decides to print for a theme\\n *    font a plugin added, and a duplicate face costs nothing.\\n *\\n * @package Toolrail\\n *\\\/\\n\\ndefine('TOOLRAIL_DEMO_FONT_BASE', 'https:\\\/\\\/raw.githubusercontent.com\\\/google\\\/fonts\\\/main\\\/ofl\\\/ebgaramond\\\/');\\n\\n\\\/**\\n * The two faces, in theme.json's fontFace shape.\\n *\\n * @return array[]\\n *\\\/\\nfunction toolrail_demo_font_faces() {\\n    return array(\\n        array(\\n            'fontFamily' => 'EB Garamond',\\n            'fontStyle'  => 'normal',\\n            'fontWeight' => '400 800',\\n            'src'        => array(TOOLRAIL_DEMO_FONT_BASE . 'EBGaramond%5Bwght%5D.ttf'),\\n        ),\\n        array(\\n            'fontFamily' => 'EB Garamond',\\n            'fontStyle'  => 'italic',\\n            'fontWeight' => '400 800',\\n            'src'        => array(TOOLRAIL_DEMO_FONT_BASE . 'EBGaramond-Italic%5Bwght%5D.ttf'),\\n        ),\\n    );\\n}\\n\\n\\\/\\\/ Append to the theme's font families rather than replace them: the\\n\\\/\\\/ theme's own fonts (Manrope for the body) must survive, and a bare\\n\\\/\\\/ update_with() would swap the whole preset.\\n\\\/\\\/\\n\\\/\\\/ SHAPE MATTERS. Inside WP_Theme_JSON the preset is keyed by origin:\\n\\\/\\\/ ['theme' => [family, family, \\u2026]]. The new family has to go INSIDE that\\n\\\/\\\/ list. Appending it beside the 'theme' key ([ 'theme' => [\\u2026], 0 => family ])\\n\\\/\\\/ still renders on the front end (PHP merges it anyway) but crashes the\\n\\\/\\\/ block editor, which iterates the origins and finds a family where it\\n\\\/\\\/ expects a list (\\\"s[c] is not iterable\\\", observed 2026-09-09).\\nadd_filter('wp_theme_json_data_theme', function ($theme_json) {\\n    $data     = $theme_json->get_data();\\n    $families = isset($data['settings']['typography']['fontFamilies'])\\n        ? $data['settings']['typography']['fontFamilies']\\n        : array();\\n    $entry = array(\\n        'name'       => 'EB Garamond',\\n        'slug'       => 'eb-garamond',\\n        'fontFamily' => '\\\"EB Garamond\\\", serif',\\n        'fontFace'   => toolrail_demo_font_faces(),\\n    );\\n    $by_origin = is_array($families) && (isset($families['theme']) || isset($families['custom']) || isset($families['default']));\\n    $list = $by_origin\\n        ? (isset($families['theme']) && is_array($families['theme']) ? $families['theme'] : array())\\n        : (is_array($families) ? $families : array());\\n    foreach ($list as $family) {\\n        if (isset($family['slug']) && 'eb-garamond' === $family['slug']) {\\n            return $theme_json;\\n        }\\n    }\\n    $list[] = $entry;\\n    if ($by_origin) {\\n        $families['theme'] = $list;\\n    } else {\\n        $families = $list;\\n    }\\n    return $theme_json->update_with(array(\\n        'version'  => isset($data['version']) ? $data['version'] : 3,\\n        'settings' => array('typography' => array('fontFamilies' => $families)),\\n    ));\\n});\\n\\n\\\/\\\/ The same two faces as an inline @font-face for the editor canvas and\\n\\\/\\\/ the front end (registration 2 in the header).\\nadd_action('enqueue_block_assets', function () {\\n    $css = '';\\n    foreach (toolrail_demo_font_faces() as $face) {\\n        $css .= sprintf(\\n            \\\"@font-face{font-family:'%s';font-style:%s;font-weight:%s;font-display:swap;src:url('%s') format('truetype');}\\\",\\n            $face['fontFamily'],\\n            $face['fontStyle'],\\n            $face['fontWeight'],\\n            esc_url($face['src'][0])\\n        );\\n    }\\n    \\\/\\\/ Styles enqueued on this hook reach the editor's canvas iframe and\\n    \\\/\\\/ the front end alike.\\n    wp_register_style('toolrail-demo-fonts', false, array(), '1.0.1');\\n    wp_enqueue_style('toolrail-demo-fonts');\\n    wp_add_inline_style('toolrail-demo-fonts', $css);\\n});\\n\"},{\"step\":\"runPHP\",\"code\":\"<?php require_once '\\\/wordpress\\\/wp-load.php';\\n\\n\\\/**\\n * Playground demo content for EditRail.\\n *\\n * This file is the SOURCE of the blueprint's `runPHP` step. `node\\n * scripts\\\/playground.js build` embeds it, verbatim, into\\n * .wordpress-org\\\/blueprints\\\/blueprint.json, so it is a real PHP file\\n * that a linter can read, not a JSON string. The wp-load guard below\\n * also lets `wp eval-file` run it on a development site that has both\\n * plugins active \\u2014 for a look at the demo post. It is written for a\\n * FRESH site: it edits user 1's editor preferences (merging, see\\n * toolrail_demo_seed_preferences) and refuses to run when post ID\\n * TOOLRAIL_DEMO_POST_ID belongs to another post. Do not run it on a\\n * site whose admin has a toolbar arrangement worth keeping.\\n *\\n * What it does, once, on a fresh Playground site:\\n *   1. Seeds the admin's editor preferences so the editor opens clean:\\n *      no welcome guide, the four default tools plus the Typography\\n *      Stylist block pinned, and that pin carrying a custom name and\\n *      description (the 1.0.1 pin-metadata feature) \\u2014 so the toolbar\\n *      itself says the tool was added by the demo.\\n *   2. Registers EB Garamond in the WordPress Font Library (the\\n *      companion mu-plugin adds it to theme.json) and ADOPTS it through\\n *      Typography Stylist's own Font Library bridge, so the font has a\\n *      real font_id and shows as the selected font in the block's\\n *      picker, the way a font an author picked would.\\n *   3. Publishes one demo post, with a fixed ID the blueprint's\\n *      landingPage points at: Typography Stylist blocks for the\\n *      headline, the tagline, every section heading and two pull\\n *      quotes, all in EB Garamond by that font_id, then core blocks\\n *      that explain the toolbar and give the Section overview\\n *      something to outline. If the post cannot be created, the script\\n *      FAILS (an uncaught exception), so the blueprint step fails\\n *      instead of landing on \\\"Invalid post ID\\\".\\n *\\n * Every OpenType claim in the copy was checked against the fonts' GSUB\\n * tables (scripts\\\/playground\\\/README.md). The italic's swash feature\\n * (swsh) substitutes every capital A\\u2013Z, so the HEADLINE and the PULL\\n * QUOTES are set in the italic with swashes on their capitals; the\\n * first pull quote adds ss01, which sets the lowercase in petite\\n * capitals, and the second adds the italic's discretionary ligatures\\n * (ck, ch). The TAGLINE and the section headings are upright with the\\n * standard ligatures only; the upright's discretionary ct and st\\n * ligatures were tried on the tagline and read as a distraction.\\n *\\n * @package Toolrail\\n *\\\/\\n\\nif (!function_exists('add_action')) {\\n    require_once '\\\/wordpress\\\/wp-load.php';\\n}\\n\\n\\\/\\\/ Fixed so the blueprint's landingPage can name it. Free on a fresh\\n\\\/\\\/ install (core seeds posts 1\\u20133); if another post already owns it,\\n\\\/\\\/ toolrail_demo_create_post() throws, so the blueprint step fails\\n\\\/\\\/ instead of landing on a different ID.\\ndefine('TOOLRAIL_DEMO_POST_ID', 2026);\\n\\n\\\/**\\n * Serialize block attributes the way Gutenberg's serializeAttributes()\\n * does: JSON with `--`, `<`, `>`, `&` and `\\\\\\\"` escaped as \\\\uXXXX, in\\n * that order, so the comment delimiter can never appear inside the\\n * payload and the editor parses the attributes it was given.\\n *\\n * @param array $attrs Block attributes.\\n * @return string\\n *\\\/\\nfunction toolrail_demo_attrs(array $attrs) {\\n    $json = wp_json_encode($attrs, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);\\n    $json = str_replace('--', '\\\\\\\\u002d\\\\\\\\u002d', $json);\\n    $json = str_replace('<', '\\\\\\\\u003c', $json);\\n    $json = str_replace('>', '\\\\\\\\u003e', $json);\\n    $json = str_replace('&', '\\\\\\\\u0026', $json);\\n    $json = str_replace('\\\\\\\\\\\"', '\\\\\\\\u0022', $json);\\n    return $json;\\n}\\n\\n\\\/**\\n * A number the way JavaScript's String(Number) prints it. save.js builds\\n * the responsive clamp() in a template literal, and block validation is\\n * a byte comparison, so PHP must print the same digits. json_encode with\\n * serialize_precision=-1 (the PHP default) uses the same shortest\\n * round-trip algorithm; only the \\\".0\\\" it adds to whole floats differs.\\n *\\n * @param float $value Number.\\n * @return string\\n *\\\/\\nfunction toolrail_demo_js_number($value) {\\n    $out = json_encode((float) $value);\\n    return '.0' === substr($out, -2) ? substr($out, 0, -2) : $out;\\n}\\n\\n\\\/**\\n * One typost\\\/block, serialized exactly as the block's save() renders it\\n * (Typography Stylist 2.2.3), with the font given by its font_id: the\\n * shape the block's own picker writes, so the editor shows the font as\\n * selected and the plugin's --font-N variable carries the family.\\n *\\n * Style order is save.js buildStyle()'s: features, font-family,\\n * font-weight, font-style, line-height, font-size, text-align. React\\n * joins \\\"prop:value\\\" pairs with \\\";\\\" and no trailing \\\";\\\".\\n *\\n * @param string $content  Inner HTML of the visual heading.\\n * @param string $tag      h1\\u2013h6 or p.\\n * @param array  $features OpenType feature tags to turn on.\\n * @param array  $args     fontId (required), fontFamily (the adopted\\n *                         entry's font_family; default EB Garamond),\\n *                         fontStyle ('italic'), fontWeight, lineHeight,\\n *                         fontSizeMin\\\/Preferred\\\/Max, textAlign.\\n * @return string Block markup.\\n *\\\/\\nfunction toolrail_demo_typost($content, $tag, array $features, array $args) {\\n    $font_id = (int) $args['fontId'];\\n    $family  = isset($args['fontFamily']) ? $args['fontFamily'] : '\\\"EB Garamond\\\", serif';\\n    $style   = isset($args['fontStyle']) ? $args['fontStyle'] : '';\\n    $weight  = isset($args['fontWeight']) ? (string) $args['fontWeight'] : '400';\\n    $line    = isset($args['lineHeight']) ? (float) $args['lineHeight'] : 0;\\n    $min     = (int) $args['fontSizeMin'];\\n    $pref    = (int) $args['fontSizePreferred'];\\n    $max     = (int) $args['fontSizeMax'];\\n    $align   = isset($args['textAlign']) ? $args['textAlign'] : '';\\n\\n    $css = array();\\n    if ($features) {\\n        \\\/\\\/ Inside a style ATTRIBUTE React writes the feature tag's quotes\\n        \\\/\\\/ as &quot; \\u2014 a raw quote ends the attribute early, and the\\n        \\\/\\\/ editor's byte comparison then sees an empty style (verified in\\n        \\\/\\\/ Playground: \\\"Expected attribute style \\u2026 saw font-feature-settings:\\\").\\n        $css[] = 'font-feature-settings:' . implode(', ', array_map(\\n            static function ($f) {\\n                return '&quot;' . $f . '&quot; 1';\\n            },\\n            $features\\n        ));\\n    }\\n    $css[] = 'font-family:var(--font-' . $font_id . ')';\\n    $css[] = 'font-weight:' . $weight;\\n    if ($style) {\\n        $css[] = 'font-style:' . $style;\\n    }\\n    if ($line) {\\n        $css[] = 'line-height:' . toolrail_demo_js_number($line);\\n    }\\n    $css[] = sprintf(\\n        'font-size:clamp(%dpx, %srem + %svw, %dpx)',\\n        $min,\\n        toolrail_demo_js_number($pref \\\/ 16),\\n        toolrail_demo_js_number((($max - $min) \\\/ (1920 - 320)) * 100),\\n        $max\\n    );\\n    if ($align) {\\n        $css[] = 'text-align:' . $align;\\n    }\\n\\n    $attrs = array('content' => $content);\\n    if ('h2' !== $tag) {\\n        $attrs['tagName'] = $tag;\\n    }\\n    if ($features) {\\n        $attrs['features'] = array_values($features);\\n    }\\n    \\\/\\\/ Both the id and the family string, the way the picker writes them\\n    \\\/\\\/ when an author chooses a Library font. The released block's edit\\n    \\\/\\\/ view gates its font-family on the string (fixed in the plugin's\\n    \\\/\\\/ source since); save() prefers the id either way, so the saved\\n    \\\/\\\/ markup is identical apart from the data-font attribute.\\n    $attrs['fontId']     = $font_id;\\n    $attrs['fontFamily'] = $family;\\n    if ($style) {\\n        $attrs['fontStyle'] = $style;\\n    }\\n    $attrs['fontSize']          = 'responsive';\\n    $attrs['fontSizeMin']       = $min;\\n    $attrs['fontSizePreferred'] = $pref;\\n    $attrs['fontSizeMax']       = $max;\\n    if ('400' !== $weight) {\\n        $attrs['fontWeight'] = $weight;\\n    }\\n    if ($align) {\\n        $attrs['textAlign'] = $align;\\n    }\\n    if ($line) {\\n        $attrs['lineHeight'] = $line;\\n    }\\n\\n    \\\/\\\/ save.js: <br> becomes a space, then every tag is stripped, for the\\n    \\\/\\\/ screen-reader twin that keeps the heading in the outline.\\n    $clean = preg_replace('\\\/<br\\\\b[^>]*>\\\/i', ' ', $content);\\n    $clean = preg_replace('\\\/<[^>]*>\\\/', '', $clean);\\n\\n    return '<!-- wp:typost\\\/block ' . toolrail_demo_attrs($attrs) . ' -->' . \\\"\\\\n\\\"\\n        . '<div class=\\\"wp-block-typost\\\">'\\n        . '<' . $tag . ' class=\\\"visually-hidden\\\">' . $clean . '<\\\/' . $tag . '>'\\n        . '<' . $tag . ' style=\\\"' . implode(';', $css) . '\\\" class=\\\"typost-styled\\\" aria-hidden=\\\"true\\\" data-font=\\\"' . esc_attr($family) . '\\\" data-font-id=\\\"' . $font_id . '\\\">' . $content . '<\\\/' . $tag . '>'\\n        . '<\\\/div>' . \\\"\\\\n\\\"\\n        . '<!-- \\\/wp:typost\\\/block -->';\\n}\\n\\n\\\/**\\n * A core paragraph block.\\n *\\n * @param string $html Inner HTML.\\n * @return string Block markup.\\n *\\\/\\nfunction toolrail_demo_paragraph($html) {\\n    return \\\"<!-- wp:paragraph -->\\\\n<p>\\\" . $html . \\\"<\\\/p>\\\\n<!-- \\\/wp:paragraph -->\\\";\\n}\\n\\n\\\/**\\n * A core heading block; level 2 is the block default and gets no attribute.\\n *\\n * @param int    $level 1\\u20136.\\n * @param string $text  Inner HTML.\\n * @return string Block markup.\\n *\\\/\\nfunction toolrail_demo_heading($level, $text) {\\n    $tag = 'h' . (int) $level;\\n    $attrs = 2 === (int) $level ? '' : ' ' . toolrail_demo_attrs(array('level' => (int) $level));\\n    return '<!-- wp:heading' . $attrs . ' -->' . \\\"\\\\n\\\"\\n        . '<' . $tag . ' class=\\\"wp-block-heading\\\">' . $text . '<\\\/' . $tag . '>' . \\\"\\\\n\\\"\\n        . '<!-- \\\/wp:heading -->';\\n}\\n\\n\\\/**\\n * A core list block with one list-item block per entry.\\n *\\n * @param string[] $items Inner HTML of each item.\\n * @return string Block markup.\\n *\\\/\\nfunction toolrail_demo_list(array $items) {\\n    $out = \\\"<!-- wp:list -->\\\\n<ul class=\\\\\\\"wp-block-list\\\\\\\">\\\";\\n    foreach ($items as $item) {\\n        $out .= \\\"<!-- wp:list-item -->\\\\n<li>\\\" . $item . \\\"<\\\/li>\\\\n<!-- \\\/wp:list-item -->\\\\n\\\\n\\\";\\n    }\\n    return rtrim($out) . \\\"<\\\/ul>\\\\n<!-- \\\/wp:list -->\\\";\\n}\\n\\n\\\/**\\n * The demo post's blocks.\\n *\\n * Typography Stylist blocks carry the headline, the tagline, every\\n * section heading and the two pull quotes, all in EB Garamond by the\\n * adopted font_id; the headline and the quotes are the italic with its\\n * swash capitals. The rest is core: paragraphs, a list, a group with a\\n * heading and two paragraphs (so the Section overview has something to\\n * outline and step into), a separator. Structure and copy owner-edited\\n * 2026-09-09: the Section overview and the extension API come before\\n * the pinning instructions, so the toolbar reads as more than a block\\n * inserter; and wherever the copy describes what the toolbar inserts,\\n * it says core blocks, theme and plugin blocks, and patterns, never\\n * \\\"core blocks\\\" alone, because pinning custom blocks is the point.\\n *\\n * @return string Post content.\\n *\\\/\\nfunction toolrail_demo_content() {\\n    $id = toolrail_demo_font_id();\\n    $blocks = array();\\n\\n    \\\/\\\/ Section headings: upright, standard ligatures only.\\n    $heading = static function ($text) use ($id) {\\n        return toolrail_demo_typost($text, 'h2', array('liga'), array(\\n            'fontId'            => $id,\\n            'lineHeight'        => 1.3,\\n            'fontSizeMin'       => 20,\\n            'fontSizePreferred' => 28,\\n            'fontSizeMax'       => 40,\\n        ));\\n    };\\n\\n    \\\/\\\/ Headline: the italic's swash capitals (swsh substitutes every\\n    \\\/\\\/ capital A\\u2013Z) draw the E and the R with their flourished forms.\\n    $blocks[] = toolrail_demo_typost('EditRail', 'h2', array('swsh'), array(\\n        'fontId'            => $id,\\n        'fontStyle'         => 'italic',\\n        'fontWeight'        => '500',\\n        'lineHeight'        => 1.05,\\n        'fontSizeMin'       => 48,\\n        'fontSizePreferred' => 80,\\n        'fontSizeMax'       => 120,\\n    ));\\n\\n    \\\/\\\/ Tagline, upright, standard ligatures only: the upright's\\n    \\\/\\\/ discretionary ligatures (ct, st) were tried and read as a\\n    \\\/\\\/ distraction at this size (owner call, 2026-09-09).\\n    $blocks[] = toolrail_demo_typost(\\n        'A graphics-editor-style toolbar for rapid content creation.',\\n        'p',\\n        array('liga'),\\n        array(\\n            'fontId'            => $id,\\n            'lineHeight'        => 1.3,\\n            'fontSizeMin'       => 20,\\n            'fontSizePreferred' => 28,\\n            'fontSizeMax'       => 40,\\n        )\\n    );\\n\\n    $blocks[] = toolrail_demo_paragraph(\\n        'EditRail puts a graphics-editor toolbar in the block editor. It docks to the left edge by default, and it can be dragged to any edge or floated as a palette. Every setting is per user, so each author keeps the arrangement that suits them.'\\n    );\\n    $blocks[] = toolrail_demo_paragraph(\\n        'The toolbar adds nothing of its own to a post. What it inserts is whatever you pinned: a core block, a block from your theme or from any plugin, or a pattern, saved exactly as the editor&#8217;s own inserter would save it. Deactivate the plugin and this post reads and edits exactly as before. The toolbar is a way of working, not a format.'\\n    );\\n\\n    $blocks[] = $heading('More than a block inserter');\\n    $blocks[] = toolrail_demo_paragraph(\\n        'The tools at the top of the toolbar are pins: blocks and patterns you chose, from core, from your theme, or from any plugin. They work the way a drawing tool does: select one, then click or drag in the canvas where it should go. It lands at that point and the toolbar returns to Select. Hold Shift while you click to keep the tool armed. Below the pins sit tools that are not blocks at all: the Section overview, and any tool another plugin adds.'\\n    );\\n\\n    $blocks[] = $heading('The Section overview');\\n    $blocks[] = toolrail_demo_paragraph(\\n        'The <strong>Section overview<\\\/strong> zooms the canvas out and outlines every top-level block, with a name tag in the corner: a way to find your place in a long document and to reorganize it. Drag an outline to move it, or click it for arrow buttons. <strong>Reorder inside<\\\/strong> steps into one section, such as the group below, and reorders its blocks the same way. Every move works by keyboard and is announced to screen readers.'\\n    );\\n\\n    $blocks[] = '<!-- wp:group ' . toolrail_demo_attrs(array('layout' => array('type' => 'constrained'))) . ' -->' . \\\"\\\\n\\\"\\n        . '<div class=\\\"wp-block-group\\\">'\\n        . toolrail_demo_heading(3, 'A group to step into')\\n        . \\\"\\\\n\\\\n\\\"\\n        . toolrail_demo_paragraph('Open the Section overview, click this group&#8217;s outline, then choose Reorder inside. The heading and this paragraph get outlines of their own.')\\n        . \\\"\\\\n\\\\n\\\"\\n        . toolrail_demo_paragraph('Move them with the arrows, or drag them. Up one level takes you back out.')\\n        . '<\\\/div>' . \\\"\\\\n\\\"\\n        . '<!-- \\\/wp:group -->';\\n\\n    $blocks[] = $heading('Pin your own tools');\\n    $blocks[] = toolrail_demo_paragraph(\\n        'Pin anything the editor can drag: core blocks, your theme&#8217;s blocks, blocks from any plugin, and patterns, your own or the theme&#8217;s. Drag it onto the toolbar from the editor&#8217;s own inserter (the <strong>+<\\\/strong> at the top left), from the List View, or from the canvas, or search for it in Toolbar settings. A block dragged from the canvas can also be saved as a pattern, with its settings and contents, and pinned as that.'\\n    );\\n\\n    \\\/\\\/ Pull quote, italic: swash capitals on every capital, and ss01 sets\\n    \\\/\\\/ the lowercase in the italic's petite capitals, a title-page\\n    \\\/\\\/ pairing the two features were drawn for. Sits right under the\\n    \\\/\\\/ pinning paragraph it sums up (owner call, 2026-09-09).\\n    $blocks[] = '<!-- wp:quote -->' . \\\"\\\\n\\\"\\n        . '<blockquote class=\\\"wp-block-quote\\\">'\\n        . toolrail_demo_typost('Any Block Can Be Added To The Toolbar', 'p', array('swsh', 'ss01'), array(\\n            'fontId'            => $id,\\n            'fontStyle'         => 'italic',\\n            'lineHeight'        => 1.15,\\n            'fontSizeMin'       => 28,\\n            'fontSizePreferred' => 40,\\n            'fontSizeMax'       => 56,\\n        ))\\n        . '<\\\/blockquote>' . \\\"\\\\n\\\"\\n        . '<!-- \\\/wp:quote -->';\\n    $blocks[] = toolrail_demo_paragraph(\\n        'A pinned tool can carry your own name, description and icon. Open <strong>Toolbar settings<\\\/strong> from the gear at the end of the toolbar, find the tool under <strong>Pinned tools<\\\/strong>, and choose <strong>Edit<\\\/strong>. Empty fields use the block&#8217;s own. Only your toolbar changes.'\\n    );\\n    $blocks[] = toolrail_demo_paragraph(\\n        'The Typography Stylist block on this toolbar is pinned as <strong>Typography Stylist (added by this demo)<\\\/strong>. That name, and the description in its tooltip, are the demo&#8217;s, not the block&#8217;s. Click it, then click in the canvas, to add a block of your own. The headings and the pull quotes in this post are Typography Stylist blocks set in EB Garamond: the headline and the quotes use the italic&#8217;s swash capitals, the first quote sets its lowercase in petite capitals, and the second adds the italic&#8217;s discretionary ligatures.'\\n    );\\n\\n    $blocks[] = $heading('Built to be extended');\\n    $blocks[] = toolrail_demo_paragraph(\\n        'Other plugins can put their own tools on the toolbar. One PHP filter declares a plugin as a provider, and a small JavaScript API registers each tool: its icon, its flyout, and what happens when it is armed, including tools that are modes rather than blocks, such as picking a block on the canvas. A plugin can also keep its own settings on a pinned tool; they travel with saved sets and go when the tool is unpinned. Whatever a tool inserts, from any plugin, is saved the way that block or pattern is always saved, so the post never depends on the toolbar.'\\n    );\\n\\n    $blocks[] = $heading('Try it now');\\n    $blocks[] = toolrail_demo_list(array(\\n        'Click <strong>Text<\\\/strong> on the toolbar, then click below this list. A paragraph appears where you clicked.',\\n        'Drag a tool from the toolbar into the canvas. The editor&#8217;s own drop line shows where it will land.',\\n        'Open the editor&#8217;s block inserter (the <strong>+<\\\/strong> button at the top left), then drag any block or pattern from that list onto the toolbar. It is pinned as a new tool.',\\n        'Drag a block from the canvas onto the toolbar to pin its type, or to save it as a pattern and pin that.',\\n        'Open the <strong>Section overview<\\\/strong> and drag this list above the heading before it.',\\n        'Press Escape at any time to return to Select.',\\n    ));\\n\\n    \\\/\\\/ Pull quote, italic: a swash on every capital of the title case, and\\n    \\\/\\\/ the italic's discretionary ligatures on ck (Pick) and ch (Reach).\\n    $blocks[] = '<!-- wp:quote -->' . \\\"\\\\n\\\"\\n        . '<blockquote class=\\\"wp-block-quote\\\">'\\n        . toolrail_demo_typost('Pick What You Reach For. Move The Toolbar Where Your Hand Wants It.', 'p', array('swsh', 'dlig'), array(\\n            'fontId'            => $id,\\n            'fontStyle'         => 'italic',\\n            'lineHeight'        => 1.2,\\n            'fontSizeMin'       => 26,\\n            'fontSizePreferred' => 36,\\n            'fontSizeMax'       => 52,\\n        ))\\n        . '<cite>What the toolbar is for<\\\/cite>'\\n        . '<\\\/blockquote>' . \\\"\\\\n\\\"\\n        . '<!-- \\\/wp:quote -->';\\n\\n    $blocks[] = \\\"<!-- wp:separator -->\\\\n<hr class=\\\\\\\"wp-block-separator has-alpha-channel-opacity\\\\\\\"\\\/>\\\\n<!-- \\\/wp:separator -->\\\";\\n\\n    $blocks[] = toolrail_demo_paragraph(\\n        'More in the Help panel: the <strong>?<\\\/strong> next to the gear explains inserting, pinning, moving the toolbar, the keyboard model, saved sets, and what a highlighted tool means. Nothing in this Playground is saved anywhere but this browser tab.'\\n    );\\n\\n    return implode(\\\"\\\\n\\\\n\\\", $blocks);\\n}\\n\\n\\\/**\\n * Seed the admin's per-user editor preferences.\\n *\\n * Both plugins keep their state in core's `{prefix}persisted_preferences`\\n * user meta: one PHP array of scopes. The rail's scope is `toolrail`,\\n * every value a string (its readKey\\\/writeKey contract). Both lift\\n * stamps are set so the rail's one-time migrations never run against\\n * this seeded list. `_modified` is bumped so a stale browser copy of\\n * the array (Playground tabs are fresh, but the rule is the rule) loses\\n * the comparison core's persistence layer makes.\\n *\\n * MERGES, never replaces: on a fresh site the result is the four\\n * defaults plus the Typography Stylist pin; on a site with an existing\\n * arrangement the pin is appended, its label added, and every other\\n * key in the scope (dock, saved sets, colors, the sibling plugins'\\n * `toolrail-ext:*` keys) is left as it was.\\n *\\n * The write is verified by reading the row back, not by update_user_meta()'s\\n * return value: that is false both when the write fails AND when the stored\\n * value is already identical, and this script may run twice on one site\\n * (idempotent by design), where a same-second re-run stores the same array.\\n *\\n * @param int $user_id The admin.\\n * @return void\\n * @throws RuntimeException When the seeded scope does not read back.\\n *\\\/\\nfunction toolrail_demo_seed_preferences($user_id) {\\n    global $wpdb;\\n    $meta_key = $wpdb->get_blog_prefix() . 'persisted_preferences';\\n    $prefs = get_user_meta($user_id, $meta_key, true);\\n    if (!is_array($prefs)) {\\n        $prefs = array();\\n    }\\n    \\\/\\\/ The editor's welcome guide, under the scope each editor build uses.\\n    $prefs['core']['welcomeGuide'] = false;\\n    $prefs['core\\\/edit-post']['welcomeGuide'] = false;\\n\\n    $scope = isset($prefs['toolrail']) && is_array($prefs['toolrail']) ? $prefs['toolrail'] : array();\\n\\n    $slots = isset($scope['toolrail-quick-slots']) ? json_decode($scope['toolrail-quick-slots'], true) : null;\\n    if (!is_array($slots)) {\\n        $slots = array('core\\\/group', 'core\\\/paragraph', 'core\\\/heading', 'core\\\/image');\\n    }\\n    if (!in_array('typost\\\/block', $slots, true)) {\\n        $slots[] = 'typost\\\/block';\\n    }\\n\\n    $meta = isset($scope['toolrail-pin-meta']) ? json_decode($scope['toolrail-pin-meta'], true) : null;\\n    if (!is_array($meta)) {\\n        $meta = array();\\n    }\\n    $meta['typost\\\/block'] = array(\\n        'title'       => 'Typography Stylist (added by this demo)',\\n        'description' => 'This demo pinned the Typography Stylist block for you and gave it this name. Click it, then click in the canvas. Change or remove it under Pinned tools in Toolbar settings.',\\n    );\\n\\n    $scope['toolrail-quick-slots']    = wp_json_encode(array_values($slots), JSON_UNESCAPED_SLASHES);\\n    $scope['toolrail-slots-migrated'] = '1';\\n    $scope['toolrail-group-seeded']   = '1';\\n    $scope['toolrail-pin-meta']       = wp_json_encode($meta, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);\\n\\n    $prefs['toolrail']  = $scope;\\n    $prefs['_modified'] = gmdate('Y-m-d\\\\TH:i:s') . '.000Z';\\n    update_user_meta($user_id, $meta_key, $prefs);\\n    wp_cache_delete($user_id, 'user_meta');\\n    $stored = get_user_meta($user_id, $meta_key, true);\\n    if (!is_array($stored) || !isset($stored['toolrail']) || $stored['toolrail'] !== $scope) {\\n        throw new RuntimeException(sprintf(\\n            'EditRail demo: the toolbar preferences did not persist to %s for user %d.',\\n            $meta_key,\\n            $user_id\\n        ));\\n    }\\n}\\n\\n\\\/**\\n * EB Garamond's Typography Stylist font_id, adopting the font on first use.\\n *\\n * The companion mu-plugin registers EB Garamond in theme.json, which is\\n * where Typography Stylist's Font Library bridge reads Library fonts\\n * from. Adopting it is what the block's picker does when an author\\n * chooses a Library font: it allocates a numeric font_id, and the\\n * plugin then emits `--font-N` for it (aliased to the WordPress preset\\n * variable). Blocks saved by that id are byte-identical to picked ones,\\n * so the editor shows EB Garamond as the selected font. The editor's\\n * font list is cached per user for an hour; the cache is cleared so the\\n * first editor load sees the adoption.\\n *\\n * @return int The font_id.\\n * @throws RuntimeException When Typography Stylist or the font is missing.\\n *\\\/\\nfunction toolrail_demo_font_id() {\\n    static $font_id = null;\\n    if (null !== $font_id) {\\n        return $font_id;\\n    }\\n    if (!class_exists('Typost')) {\\n        throw new RuntimeException('EditRail demo: Typography Stylist is not active; the demo post needs its block.');\\n    }\\n    $entry = Typost::get_instance()->font_library_bridge()->adopt_library_font('eb-garamond');\\n    if (!is_array($entry) || empty($entry['font_id'])) {\\n        throw new RuntimeException('EditRail demo: EB Garamond is not in the Font Library; the demo fonts mu-plugin did not register it.');\\n    }\\n    delete_transient('typost_editor_data_1');\\n    $font_id = (int) $entry['font_id'];\\n    return $font_id;\\n}\\n\\n\\\/**\\n * Publish the demo post at the fixed ID, once.\\n *\\n * Runs with the admin as the current user and kses lifted: the\\n * blueprint's runPHP has no user, and kses would otherwise strip the\\n * Typography Stylist block's font-feature-settings (not on kses's safe\\n * CSS list) and invalidate the block in the editor.\\n *\\n * Idempotent: the demo post already at that ID is left alone. Any other\\n * outcome that leaves landingPage pointing at nothing \\u2014 the ID taken by\\n * another post, or wp_insert_post() refusing \\u2014 THROWS, so the blueprint\\n * step fails with the reason instead of the preview opening on\\n * \\\"Invalid post ID\\\" (PR review 2026-09-04, finding 4).\\n *\\n * @param int $user_id The admin.\\n * @return int The post ID.\\n * @throws RuntimeException When the post cannot be created at TOOLRAIL_DEMO_POST_ID.\\n *\\\/\\nfunction toolrail_demo_create_post($user_id) {\\n    $existing = get_post(TOOLRAIL_DEMO_POST_ID);\\n    if ($existing) {\\n        if ('editrail-demo' === $existing->post_name) {\\n            return (int) $existing->ID;\\n        }\\n        throw new RuntimeException(sprintf(\\n            'EditRail demo: post ID %d is already used by \\\"%s\\\"; the blueprint landing page needs it.',\\n            TOOLRAIL_DEMO_POST_ID,\\n            $existing->post_title\\n        ));\\n    }\\n    wp_set_current_user($user_id);\\n    kses_remove_filters();\\n    $id = wp_insert_post(wp_slash(array(\\n        'import_id'      => TOOLRAIL_DEMO_POST_ID,\\n        'post_type'      => 'post',\\n        'post_status'    => 'publish',\\n        'post_author'    => $user_id,\\n        'post_title'     => 'Try EditRail',\\n        'post_name'      => 'editrail-demo',\\n        'post_content'   => toolrail_demo_content(),\\n        'comment_status' => 'closed',\\n        'ping_status'    => 'closed',\\n    )), true);\\n    kses_init_filters();\\n    if (is_wp_error($id)) {\\n        throw new RuntimeException('EditRail demo: the demo post could not be created: ' . $id->get_error_message());\\n    }\\n    if ((int) $id !== TOOLRAIL_DEMO_POST_ID) {\\n        throw new RuntimeException(sprintf(\\n            'EditRail demo: the demo post landed at ID %d, not %d; the blueprint landing page would miss it.',\\n            $id,\\n            TOOLRAIL_DEMO_POST_ID\\n        ));\\n    }\\n    return (int) $id;\\n}\\n\\ntoolrail_demo_seed_preferences(1);\\ntoolrail_demo_create_post(1);\\n\"},{\"step\":\"login\",\"username\":\"admin\"}]}"}},"all_blocks":[],"tagged_versions":["1.0.1"],"block_files":[],"assets_screenshots":{"screenshot-1.png":{"filename":"screenshot-1.png","revision":3691627,"resolution":"1","location":"assets","locale":"","width":1440,"height":900},"screenshot-2.png":{"filename":"screenshot-2.png","revision":3691627,"resolution":"2","location":"assets","locale":"","width":1440,"height":900},"screenshot-3.png":{"filename":"screenshot-3.png","revision":3691627,"resolution":"3","location":"assets","locale":"","width":1440,"height":900},"screenshot-4.png":{"filename":"screenshot-4.png","revision":3691627,"resolution":"4","location":"assets","locale":"","width":1440,"height":900},"screenshot-5.png":{"filename":"screenshot-5.png","revision":3691627,"resolution":"5","location":"assets","locale":"","width":1440,"height":900},"screenshot-6.png":{"filename":"screenshot-6.png","revision":3691627,"resolution":"6","location":"assets","locale":"","width":1440,"height":900},"screenshot-7.png":{"filename":"screenshot-7.png","revision":3691627,"resolution":"7","location":"assets","locale":"","width":1440,"height":900}},"screenshots":{"1":"The toolbar docked at the left of the canvas. The Heading tool is armed and waits for a click.","2":"Toolbar settings: the pinned tools, and a search that adds any block or pattern to the toolbar.","3":"Section overview: the whole page zoomed out, with an outline and a name tag for each section.","4":"Tool names on, in the Light appearance. The toolbar widens to show a label beside each icon.","5":"The toolbar docked at the top of the editor as a horizontal bar.","6":"Edit a pinned tool: give it your own name, description and icon. Browse icons lists every Dashicon.","7":"Toolbar help: what each tool does, and every keyboard shortcut."}},"plugin_section":[],"plugin_tags":[148857,1541,3972],"plugin_category":[59],"plugin_contributors":[252841],"plugin_business_model":[],"class_list":["post-365047","plugin","type-plugin","status-publish","hentry","plugin_tags-block-editor","plugin_tags-toolbar","plugin_tags-tools","plugin_category-utilities-and-tools","plugin_contributors-matthewneilcowan","plugin_committers-matthewneilcowan"],"banners":{"banner":"https:\/\/ps.w.org\/editrail\/assets\/banner-772x250.png?rev=3691627","banner_2x":"https:\/\/ps.w.org\/editrail\/assets\/banner-1544x500.png?rev=3691627","banner_rtl":false,"banner_2x_rtl":false},"icons":{"svg":"https:\/\/ps.w.org\/editrail\/assets\/icon.svg?rev=3691627","icon":"https:\/\/ps.w.org\/editrail\/assets\/icon.svg?rev=3691627","icon_2x":false,"generated":false},"screenshots":[{"src":"https:\/\/ps.w.org\/editrail\/assets\/screenshot-1.png?rev=3691627","caption":"The toolbar docked at the left of the canvas. The Heading tool is armed and waits for a click."},{"src":"https:\/\/ps.w.org\/editrail\/assets\/screenshot-2.png?rev=3691627","caption":"Toolbar settings: the pinned tools, and a search that adds any block or pattern to the toolbar."},{"src":"https:\/\/ps.w.org\/editrail\/assets\/screenshot-3.png?rev=3691627","caption":"Section overview: the whole page zoomed out, with an outline and a name tag for each section."},{"src":"https:\/\/ps.w.org\/editrail\/assets\/screenshot-4.png?rev=3691627","caption":"Tool names on, in the Light appearance. The toolbar widens to show a label beside each icon."},{"src":"https:\/\/ps.w.org\/editrail\/assets\/screenshot-5.png?rev=3691627","caption":"The toolbar docked at the top of the editor as a horizontal bar."},{"src":"https:\/\/ps.w.org\/editrail\/assets\/screenshot-6.png?rev=3691627","caption":"Edit a pinned tool: give it your own name, description and icon. Browse icons lists every Dashicon."},{"src":"https:\/\/ps.w.org\/editrail\/assets\/screenshot-7.png?rev=3691627","caption":"Toolbar help: what each tool does, and every keyboard shortcut."}],"raw_content":"<!--section=description-->\n<p>EditRail adds a toolbar to the post editor, docked to the left edge by default, movable to any edge or floating as a palette. Selecting a tool arms it: the next click in the canvas inserts that tool's block at the click point, then the rail returns to Select (Shift-click keeps the tool armed).<\/p>\n\n<p>Everything the rail inserts is an ordinary core block. Deactivating this plugin changes nothing about how authored content renders or stays editable.<\/p>\n\n<ul>\n<li>Pin any block type as a quick-insert tool: search for it in Toolbar settings, drag it from the inserter onto the rail, or use \"Pin to toolbar\" in the block's options menu. Pins, the toolbar position and saved sets are saved to your user account on the site \u2014 set the toolbar up once and it follows you across browsers and devices.<\/li>\n<li>Give a pinned tool your own name, description or icon: use Edit beside the tool under \"Pinned tools\" in Toolbar settings. Empty fields use the block's own. This helps when you pin several patterns, or when a site sets up a tool for you. The icon is up to three characters, or a Dashicon picked from a searchable list. Saved sets keep them, and set files carry them along.<\/li>\n<li>Built-in tools: Select and Section overview. Group, Text, Heading and Image are ordinary pinned blocks, so you can reorder or remove them like anything else you pin. If you unpin the defaults, a \"Restore default tools\" button in Toolbar settings brings back the missing ones without touching your own pins.<\/li>\n<li>While a tool is armed, the editor's own \"+\" buttons (between blocks, and beside an empty block) are hidden, so your click goes to the armed tool. The drop line you see while you drag a block is not affected. A checkbox under \"Inserting\" in Toolbar settings turns this off.<\/li>\n<li>Drag a tool from the toolbar into the canvas to place its block where you drop it. Clicking a tool still arms it.<\/li>\n<li>Pin patterns too. Search under \"Add a block or pattern\" in Toolbar settings for your own patterns and the theme's and core's, or drag a pattern from the inserter onto the toolbar. An armed pattern inserts a fresh copy at the click point; a synced pattern inserts a reference, the same as the inserter does.<\/li>\n<li>Drop a block from the canvas onto the toolbar and choose: pin its block type, or save the block with its settings and contents as one of your patterns and pin that pattern. The same dialog opens from \"Save as pattern and pin to toolbar\u2026\" in the block's options menu.<\/li>\n<li>Section overview: one tool zooms the canvas out and draws an outline around every top-level block, with a small name tag in the corner. Drag an outline to reorder it, or click it to reveal reorder controls inside the lines \u2014 arrows to move it, and a \"Reorder inside\" button to step into a section and reorder its blocks the same way, with a breadcrumb back out. Select several outlines at once \u2014 Shift+click for a range, Ctrl+click (Cmd on Mac) to add or remove one, or a rectangle dragged from empty space \u2014 and the arrows or a drag then move the whole group; locked blocks show a padlock and stay where they are. Zoom with the +\/\u2212 buttons and pan long documents with the mouse wheel. Every move works by keyboard and is announced to screen readers \u2014 dragging is a shortcut, never the only way \u2014 and closing centers and selects the block you last picked, moved or stepped into (if you touched nothing, it returns you to where you were scrolled). Reordering is an ordinary editing action \u2014 the saved post is exactly what the List View would have written.<\/li>\n<li>Save pinned arrangements as named sets, and move them between sites as small JSON files (Export\/Import in Toolbar settings). A set may name blocks a site doesn't have \u2014 those stay in the set and appear when their plugin or theme is active.<\/li>\n<li>Move the toolbar where you want it: it starts on the left edge, and can dock to the right edge (past the settings side panel), to a full-width bar across the top or the bottom, or float free as a tool palette. Drag it by the grip and release near an edge to snap it there, or pick a position in Toolbar settings. Flyouts and panels open away from the docked edge \u2014 a top toolbar opens downward, a bottom one upward.<\/li>\n<li>Full keyboard operability: one tab stop, arrow keys, Home\/End, Escape disarms. Toolbar settings and the tool flyouts close on Escape and when you tab past them, so they never sit open behind you. Arrow keys follow the toolbar's orientation \u2014 Up\/Down along a vertical rail with ArrowRight opening a tool's flyout, Left\/Right along a horizontal one with ArrowDown opening the flyout. Repositioning has a keyboard path of its own in Toolbar settings, so it never depends on dragging.<\/li>\n<li>Show tool names beside the icons: a \"Tool names\" checkbox in Toolbar settings widens a vertical or floating toolbar into icon + name rows. Icon-only toolbars ask you to learn the icons; this is the way around that. If you switch often, a second checkbox adds an expand\/contract button to the toolbar itself.<\/li>\n<li>Pick the toolbar's colors: Dark (default), Light and Gray presets, or your own background + text pair. Every preset meets the WCAG contrast minimums, and with custom colors the focus ring and pressed markers are adjusted automatically so they stay visible.<\/li>\n<li>A Help panel (\"?\" next to the gear) explains inserting, pinning, moving the toolbar, the keyboard model, saved sets, and what a highlighted tool means. It never opens by itself; open it from the \"?\" or from Toolbar settings. You can hide the \"?\" from the toolbar \u2014 the panel stays available from Toolbar settings.<\/li>\n<li>Tools that need a canvas click are dimmed while the Section overview is open, with the reason in their tooltip. They stay in the keyboard order and keep their names for screen readers. Tools that open a panel stay available.<\/li>\n<li>Provider API for themes and plugins: PHP filter <code>toolrail_tool_providers<\/code> + JS <code>window.toolrail.registerTool()<\/code>. A tool can declare <code>supports: { canvas: true }<\/code> when its action needs a canvas click, so the toolbar dims it in any mode that captures the canvas. A plugin can nest its tools under a pinned tool and keep its own settings on that pin. The settings are removed when the pin is unpinned, and come back when you load a saved set that has them.<\/li>\n<\/ul>\n\n<!--section=faq-->\n<dl>\n<dt id=\"where%20is%20the%20toolbar%20position%20saved%3F\"><h3>Where is the toolbar position saved?<\/h3><\/dt>\n<dd><p>Against your user account on this site, the same as pinned blocks and saved sets. WordPress's own per-user editor preferences carry it, so a different browser or device shows the toolbar exactly where you left it once you log in. It is never site content and never affects other users. Preferences are per-site; to carry a pinned arrangement to a different site, export it as a set file and import it there.<\/p><\/dd>\n<dt id=\"can%20i%20put%20it%20back%20if%20i%20lose%20it%3F\"><h3>Can I put it back if I lose it?<\/h3><\/dt>\n<dd><p>Yes. Open Toolbar settings from the gear at the end of the toolbar and choose a position under \"Toolbar position\".<\/p><\/dd>\n<dt id=\"what%20does%20deleting%20the%20plugin%20remove%3F\"><h3>What does deleting the plugin remove?<\/h3><\/dt>\n<dd><p>Its own preferences, and nothing else. The plugin stores no options and writes nothing into posts. Deleting it from the Plugins screen removes the pinned tools, toolbar position, saved sets and colors from every user account on the site. Deactivating it keeps them, so the toolbar comes back as you left it when you activate the plugin again. In rare cases a browser holds an older local copy of the pins, the toolbar position or the saved sets (from a session where the browser's storage failed, or from a version before 0.1.6). If you install the plugin again in that browser, that copy comes back one time. Unpin what you do not want; it does not return again.<\/p><\/dd>\n\n<\/dl>\n\n<!--section=changelog-->\n<h4>1.0.1<\/h4>\n\n<ul>\n<li>Renamed the plugin to EditRail. The slug and the text domain are now <code>editrail<\/code>. The stored preferences, the JavaScript API and the PHP filter keep their names, so pinned tools, saved sets and extensions are not affected.<\/li>\n<li>Edit a pinned tool's name, description and icon from Toolbar settings. Pick the icon from a searchable list of Dashicons, or type up to three characters. Saved sets keep these, and set files carry them along.<\/li>\n<li>Plugins can keep their own settings on a pinned tool. The settings are removed when the tool is unpinned, and travel in saved sets.<\/li>\n<\/ul>\n\n<h4>1.0.0<\/h4>\n\n<ul>\n<li>First public release.<\/li>\n<\/ul>","raw_excerpt":"A movable, graphics-editor-style toolbar for the block editor. Click a tool, then click the canvas to insert a core block at that point.","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin\/365047","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin"}],"about":[{"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/types\/plugin"}],"replies":[{"embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/comments?post=365047"}],"author":[{"embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wporg\/v1\/users\/matthewneilcowan"}],"wp:attachment":[{"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/media?parent=365047"}],"wp:term":[{"taxonomy":"plugin_section","embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_section?post=365047"},{"taxonomy":"plugin_tags","embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_tags?post=365047"},{"taxonomy":"plugin_category","embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_category?post=365047"},{"taxonomy":"plugin_contributors","embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_contributors?post=365047"},{"taxonomy":"plugin_business_model","embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_business_model?post=365047"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}