From 031e9ac3e00127856b976280e572bc2c9c909625 Mon Sep 17 00:00:00 2001 From: John McLear Date: Sat, 19 Sep 2026 17:44:38 +0100 Subject: [PATCH] fix: add the style dropdown to the file menu MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ep_headings2 only rendered its dropdown into the editbar, so with ep_file_menu_toolbar installed there was no way to set a heading from the Format menu — the plugin never hooked any of the file menu's blocks. Add `templates/fileMenu.ejs` and hook `eejsBlock_dd_format_block`, the paragraph formatting group of the Format menu (right below Outdent), which is where a line level style belongs. The template offers the same options as the editbar dropdown and reuses the existing `ep_headings.*` localization ids. The file menu select is found by class instead of repeating the editbar select's id, and the client now binds and updates `#heading-selection, select.heading-selection` so both dropdowns apply a style and both follow the caret. This is the same shape ep_font_size and ep_font_color already use for their second dropdown. Needs ep_file_menu_toolbar >= the release that adds `dd_format_block` (ether/ep_file_menu_toolbar#121). Fixes #52 Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_012kA75NPq8nGRidAwhPXeCi --- ep.json | 1 + index.js | 2 + static/js/index.js | 9 ++- static/tests/backend/specs/filemenu.js | 55 +++++++++++++++++++ .../frontend-new/specs/file_menu.spec.ts | 55 +++++++++++++++++++ templates/fileMenu.ejs | 16 ++++++ 6 files changed, 135 insertions(+), 3 deletions(-) create mode 100644 static/tests/backend/specs/filemenu.js create mode 100644 static/tests/frontend-new/specs/file_menu.spec.ts create mode 100644 templates/fileMenu.ejs diff --git a/ep.json b/ep.json index 98d1235..d0a6ac1 100644 --- a/ep.json +++ b/ep.json @@ -14,6 +14,7 @@ }, "hooks": { "eejsBlock_editbarMenuLeft": "ep_headings2/index", + "eejsBlock_dd_format_block": "ep_headings2/index", "collectContentPre": "ep_headings2/static/js/shared", "collectContentPost": "ep_headings2/static/js/shared", "ccRegisterBlockElements": "ep_headings2/static/js/shared", diff --git a/index.js b/index.js index d038be2..af50b64 100644 --- a/index.js +++ b/index.js @@ -18,5 +18,7 @@ const headingsExport = lineAttributeExport({ }); exports.eejsBlock_editbarMenuLeft = template('ep_headings2/templates/editbarButtons.ejs'); +// ep_file_menu_toolbar: paragraph formatting group of the Format menu. +exports.eejsBlock_dd_format_block = template('ep_headings2/templates/fileMenu.ejs'); exports.stylesForExport = headingsExport.stylesForExport; exports.getLineHTMLForExport = headingsExport.getLineHTMLForExport; diff --git a/static/js/index.js b/static/js/index.js index f44d9d4..a349e85 100644 --- a/static/js/index.js +++ b/static/js/index.js @@ -22,7 +22,9 @@ exports.aceDomLineProcessLineAttributes = headings.aceDomLineProcessLineAttribut // fixed #130. exports.postAceInit = (hookName, context) => { toolbarSelect({ - selector: '#heading-selection', + // The editbar dropdown and, when ep_file_menu_toolbar is installed, the + // copy in the Format menu. + selector: '#heading-selection, select.heading-selection', context, invoke: (ace, value) => ace.ace_doInsertHeading(value), op: 'insertheading', @@ -46,7 +48,8 @@ exports.aceEditEvent = (hookName, call) => { const attributeManager = call.documentAttributeManager; const rep = call.rep; const activeAttributes = {}; - $('#heading-selection').val('dummy').niceSelect('update'); + const headingSelect = $('#heading-selection, select.heading-selection'); + headingSelect.val('dummy').niceSelect('update'); const firstLine = rep.selStart[0]; const lastLine = Math.max(firstLine, rep.selEnd[0] - ((rep.selEnd[1] === 0) ? 1 : 0)); @@ -66,7 +69,7 @@ exports.aceEditEvent = (hookName, call) => { $.each(activeAttributes, (k, attr) => { if (attr.count === totalNumberOfLines) { const ind = tags.indexOf(k); - $('#heading-selection').val(ind).niceSelect('update'); + headingSelect.val(ind).niceSelect('update'); } }); }, 250); diff --git a/static/tests/backend/specs/filemenu.js b/static/tests/backend/specs/filemenu.js new file mode 100644 index 0000000..9b1d8d5 --- /dev/null +++ b/static/tests/backend/specs/filemenu.js @@ -0,0 +1,55 @@ +'use strict'; + +const assert = require('assert').strict; +const fs = require('fs'); +const path = require('path'); +const ejs = require('ep_etherpad-lite/node_modules/ejs'); + +const root = path.resolve(__dirname, '..', '..', '..', '..'); +const render = (name) => + ejs.render(fs.readFileSync(path.join(root, 'templates', name), 'utf8'), {}); +const locales = JSON.parse(fs.readFileSync(path.join(root, 'locales', 'en.json'), 'utf8')); +const epJson = JSON.parse(fs.readFileSync(path.join(root, 'ep.json'), 'utf8')); + +// Every