Project

General

Profile

Actions

Feature #43259

open

Replace jsToolBar with a server-rendered wiki toolbar handled by a Stimulus controller

Added by Takashi Kato about 1 year ago. Updated 12 minutes ago.

Status:
New
Priority:
Normal
Category:
Code cleanup/refactoring
Target version:
Resolution:

Description

This patch is based on a branch from #43165, with minor modifications.

  • Converting the entire library to ES modules eliminates the risk of variable name collisions.
  • Refactored jstoolbar from the old prototype-based syntax to a more readable class syntax.
  • Switched DOM element creation from JavaScript to ERB, which makes preparing and cloning DOM elements easier by using the <template> element.
  • Moved all jstoolbar locales to config/locales to simplify i18n. * Added the missing tasklist locale for both ja and en.
  • Integrated the list-autofill controller into jstoolbar as both controllers are used in the same places.

Applying this patch along with #42521 enables the implementation of #40310.

This patch can be applied to r24014


Files


Related issues

Related to Redmine - Patch #37559: Use meta element instead of javascript objectNewMarius BĂLTEANUActions
Actions #1

Updated by Marius BĂLTEANU about 1 year ago

  • Assignee set to Marius BĂLTEANU
  • Target version set to 7.0.0

I'm assigning this 7.0.0 in order to review the changes.

Actions #2

Updated by Marius BĂLTEANU 8 months ago

  • File 0001-wip.patch added

Takashi Kato, I started to work on rewriting the toolbar in order to render most of the elements on the backend and connect to Stimulus JS for the actions. Here is a prof of concept only for testing purposes, it still need some work.

Any feedback is welcome!

Actions #3

Updated by Marius BĂLTEANU 8 months ago

  • File deleted (0001-wip.patch)
Actions #5

Updated by Marius BĂLTEANU 8 months ago

  • Related to Patch #37559: Use meta element instead of javascript object added
Actions #6

Updated by Takashi Kato 8 months ago

I applied the patch. I think the new toolbar design is simpler and better than before.

  • Toolbar generation is now separated into its own class
    • This has the benefit of allowing shorter method names
    • Memoization speeds up rendering on subsequent runs.
  • Key binding configuration has been moved from JavaScript to the data-action attribute in Stimulus.
  • Added configuration for a hotkey to toggle the preview.
Actions #7

Updated by Takashi Kato 8 months ago

I confirmed that subsequent renderings within the same request become faster with code like the following.

require 'test_helper'
require 'benchmark/ips'

class ViewPerformanceTest < ActionDispatch::IntegrationTest
  def setup
    @controller = ApplicationController.new
    @controller.request = ActionDispatch::TestRequest.create
    @view_context = @controller.view_context
  end

  def test_view_benchmarks
    puts "\n--- Benchmarking Render Methods ---" 

    Benchmark.ips do |x|
      x.report('Helper (Cold Start)') do
        controller = ApplicationController.new
        controller.request = ActionDispatch::TestRequest.create
        view_context = controller.view_context
        view_context.wikitoolbar(preview_url: "/users/1"){'<textarea></textarea>'}
      end

      # Execute it once beforehand to memoize it.
      @view_context.wikitoolbar(preview_url: "/users/1"){'<textarea></textarea>'}

      x.report("Helper (Memoized)") do
        @view_context.wikitoolbar(preview_url: "/users/1"){'<textarea></textarea>'}
      end

      x.compare!
    end
  end
end
--- Benchmarking Render Methods ---
ruby 3.4.7 (2025-10-08 revision 7a5688e2a2) +PRISM [x86_64-linux]
Warming up --------------------------------------
 Helper (Cold Start)    40.000 i/100ms
   Helper (Memoized)   439.000 i/100ms
Calculating -------------------------------------
 Helper (Cold Start)    401.374 (±12.7%) i/s    (2.49 ms/i) -      2.000k in   5.073074s
   Helper (Memoized)      5.138k (±11.7%) i/s  (194.61 μs/i) -     25.462k in   5.033269s

Comparison:
   Helper (Memoized):     5138.4 i/s
 Helper (Cold Start):      401.4 i/s - 12.80x  slower

Actions #8

Updated by Marius BĂLTEANU 4 months ago

  • Target version changed from 7.0.0 to 7.1.0
Actions #9

Updated by Marius BĂLTEANU 2 days ago

  • Tracker changed from Patch to Feature
  • Subject changed from Convert jstoolbar to ES modules and class syntax. to Replace jsToolBar with a server-rendered wiki toolbar handled by a Stimulus controller

I've committed a combined patch built on the work of Takashi Kato and me. It's the first in a series of changes I'd like to commit before finishing the refactoring of the wiki toolbar.

Changes made in r25352:
  • Redmine::WikiToolbar renders the toolbar: the edit/preview tabs, the buttons and the menus (code highlighting languages, table generator). Each text formatter (Textile, CommonMark) describes its buttons in Ruby.
  • One Stimulus controller handles the buttons, the menus (keyboard navigation, Escape and outside click), the keyboard shortcuts (Ctrl/Cmd+B/I/U, Ctrl/Cmd+Shift+P to toggle preview) and the preview. The preview sends the attachments and the data-preview-param fields.
  • The toolbar translations move from the jstoolbar-*.js language files to config/locales (label_wiki_toolbar_*).
  • Removed: jsToolBar (jstoolbar.js, textile.js, common_mark.js), the 49 language files and the jQuery preview handler.
Compatibility:
  • wikitoolbar_for(field_id, preview_url) is unchanged, so views and plugins that call it keep working.
  • The markup and CSS classes (jstBlock, jstTabs, jstEditor, jstElements, jstb_*, wiki-preview) are unchanged, so themes keep working.
Next changes in the series:
  • define the markup inserted by the buttons in JavaScript, per text formatter;
  • render the toolbar around its textarea with partials and a form builder method (wiki_textarea), deprecating wikitoolbar_for;

This change breaks plugins that use the jsToolBar JavaScript object directly (for example, to add buttons) and they need to be updated. Please expect a few days to finish this.

Actions #10

Updated by Marius BĂLTEANU 2 days ago

Next changes in the series:
  • define the markup inserted by the buttons in JavaScript, per text formatter;

Attached the patch that moves the markup of toolbar buttons from Ruby action parameters to app/javascript/wiki_formatting/<format>.js. Redmine::WikiToolbar will only describes the layout of the buttons.

Please let me know what do you think about this approach.

Actions #11

Updated by Florian Walchshofer 12 minutes ago

I tested r25355 and 0001-Define-the-wiki-toolbar-markup-in-JavaScript-for-eac.patch in the browser and compared them with the version before r25352.
I think moving the markup to JavaScript is a good approach: the inserted text and the cursor position are identical for all buttons with Textile and CommonMark.

I found two regressions from r25352. The attached patches apply on top of 0001-Define-the-wiki-toolbar-markup-in-JavaScript-for-eac.patch, the changed lines are the same for the current trunk:

Two additions:

document.addEventListener('wiki-toolbar:button', ({ detail: { button, editor } }) => {
  if (button === 'mark') editor.wrap('%{background:yellow}', '%')
})

With the patches applied, the unit, integration and system tests of the toolbar pass.

Actions

Also available in: Atom PDF