From 8ab2c719e3a008e53477ad81308279a041a1dce5 Mon Sep 17 00:00:00 2001 From: Mae Beale Date: Sun, 16 Aug 2026 18:04:54 -0400 Subject: [PATCH 1/3] Match story editor block spacing to the published page MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The rhino-editor's bundled trix.css zeroes out block margins inside .trix-content, so while editing, paragraphs and other blocks jammed together with no separation — yet the published story (wrapped in Tailwind `prose`) spaces them normally. That mismatch made the editor hard to read and non-WYSIWYG. Restore prose's block spacing in the editor so what you type matches what readers see. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../javascript/rhino/custom-editor.css | 45 ++++++++++++++++++- 1 file changed, 44 insertions(+), 1 deletion(-) diff --git a/app/frontend/javascript/rhino/custom-editor.css b/app/frontend/javascript/rhino/custom-editor.css index 97d6716f18..c5b7cb5da7 100644 --- a/app/frontend/javascript/rhino/custom-editor.css +++ b/app/frontend/javascript/rhino/custom-editor.css @@ -41,7 +41,50 @@ custom-rhino-editor table .column-resize-handle { top: 0; width: 4px; } -custom-rhino-editor table p { +/* WYSIWYG: trix.css zeroes block margins inside .trix-content, so the editor jams + blocks together while the rendered `prose` page spaces them. Restore prose's + spacing (values mirror @tailwindcss/typography's em defaults). */ +custom-rhino-editor .trix-content > :first-child { + margin-top: 0; +} +custom-rhino-editor .trix-content > :last-child { + margin-bottom: 0; +} +custom-rhino-editor .trix-content p, +custom-rhino-editor .trix-content ul, +custom-rhino-editor .trix-content ol { + margin: 1.25em 0; +} +custom-rhino-editor .trix-content li { + margin: 0.5em 0; +} +/* The editor wraps each list item's text in a

; the rendered page doesn't. + Zero it so the

  • margin (not the paragraph rule) controls list spacing. */ +custom-rhino-editor .trix-content li > p { + margin: 0; +} +custom-rhino-editor .trix-content blockquote { + margin: 1.6em 0; +} +custom-rhino-editor .trix-content h1 { + margin: 0 0 0.889em; +} +custom-rhino-editor .trix-content h2 { + margin: 2em 0 1em; +} +custom-rhino-editor .trix-content h3 { + margin: 1.6em 0 0.6em; +} +custom-rhino-editor .trix-content h4 { + margin: 1.5em 0 0.5em; +} +custom-rhino-editor .trix-content figure { + margin: 2em 0; +} +custom-rhino-editor .trix-content hr { + margin: 3em 0; +} +custom-rhino-editor .trix-content table p { margin: 0; } custom-rhino-editor a { From e73583de4254747413ee23ab7c7e91f1ac4a416c Mon Sep 17 00:00:00 2001 From: Mae Beale Date: Tue, 18 Aug 2026 06:19:42 -0400 Subject: [PATCH 2/3] Make rhino editor previews match the published page (WYSIWYG) Beyond block spacing, the editor diverged from the show page in ways that made rich content (event header/description) look nothing like it renders: the show page wraps rhino content in per-field prose classes the editor didn't share (full-width + centered images), and borderless grid cells ballooned in the editor from padding plus restored block margins. Parameterize rhino_editor with centered/full_width_images so each form can wrap its editor in the same prose classes its show page uses, and opt the event header/description in to match events/show. Keep the dashed grid-cell outline for editing structure but drop its padding and inner block spacing so cells preview at the published size. Co-Authored-By: Claude Opus 4.8 (1M context) --- app/frontend/javascript/rhino/custom-editor.css | 13 +++++++++++-- app/helpers/rhino_editor_helper.rb | 13 +++++++++++-- app/views/events/_form.html.erb | 4 ++-- 3 files changed, 24 insertions(+), 6 deletions(-) diff --git a/app/frontend/javascript/rhino/custom-editor.css b/app/frontend/javascript/rhino/custom-editor.css index c5b7cb5da7..af28b6d5f0 100644 --- a/app/frontend/javascript/rhino/custom-editor.css +++ b/app/frontend/javascript/rhino/custom-editor.css @@ -96,9 +96,18 @@ custom-rhino-editor a:hover { color: #1e40af; /* Tailwind blue-800 */ } -/* Dashed border for grid cells that have hasBorder=false (editor only) */ +/* Dashed border for grid cells that have hasBorder=false (editor only). Keep the + outline so the grid structure stays visible/editable, but strip the padding and + inner block spacing so the cell doesn't balloon — it previews at the size the + published page renders. */ custom-rhino-editor .grid-cell-editor[hasborder="false"] { - border: 2px dashed #ced4da; } + border: 2px dashed #ced4da; + padding: 0; +} +custom-rhino-editor .grid-cell-editor[hasborder="false"] > * { + margin-top: 0; + margin-bottom: 0; +} custom-rhino-editor span.mention { border: 1px solid #000; diff --git a/app/helpers/rhino_editor_helper.rb b/app/helpers/rhino_editor_helper.rb index abfad9bcc5..50a85bbfab 100644 --- a/app/helpers/rhino_editor_helper.rb +++ b/app/helpers/rhino_editor_helper.rb @@ -1,6 +1,9 @@ module RhinoEditorHelper # custom rhino editor with stimulus controller attached to edit raw source html - def rhino_editor(form, base_attribute_name, label: nil, hint: nil) + # centered / full_width_images make the editor wrap its content in the same prose + # classes the show page uses for this field, so editing previews match the published + # page (WYSIWYG). Pass whatever the destination view applies (see events/show). + def rhino_editor(form, base_attribute_name, label: nil, hint: nil, centered: false, full_width_images: false) object = Draper.undecorate(form.object) rhino_attr = :"rhino_#{base_attribute_name}" field_id = form.field_id(rhino_attr) @@ -75,7 +78,13 @@ def rhino_editor(form, base_attribute_name, label: nil, hint: nil) } ) - content_tag(:div, data: { controller: "rhino-source" }, class: "mb-4 prose max-w-none bg-white prose-strong:text-inherit prose-em:text-inherit") do + wrapper_class = [ + "mb-4 prose max-w-none bg-white prose-strong:text-inherit prose-em:text-inherit", + ("rhino-centered" if centered), + ("prose-img:max-w-full prose-img:w-full" if full_width_images) + ].compact.join(" ") + + content_tag(:div, data: { controller: "rhino-source" }, class: wrapper_class) do safe_join([ label_tag, editor, diff --git a/app/views/events/_form.html.erb b/app/views/events/_form.html.erb index a2d7f566d3..ad3c3f771a 100644 --- a/app/views/events/_form.html.erb +++ b/app/views/events/_form.html.erb @@ -549,14 +549,14 @@ <%= f.input :autoshow_title, as: :boolean, label: "Show title on event show page" %>
    - <%= rhino_editor(f, :header, label: "Header content") %> + <%= rhino_editor(f, :header, label: "Header content", centered: true, full_width_images: true) %> <% if f.object.errors[:rhino_header].present? %>
    Field <%= f.object.errors[:rhino_header].first %>
    <% end %>
    -
    <%= rhino_editor(f, :description, label: "Description") %>
    +
    <%= rhino_editor(f, :description, label: "Description", full_width_images: true) %>
    From f3216190b5f85719ab3d331445681a03888f49a4 Mon Sep 17 00:00:00 2001 From: Mae Beale Date: Tue, 18 Aug 2026 11:16:44 -0400 Subject: [PATCH 3/3] Drop per-form editor styling; centering is content-driven MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Review feedback: the rhino_editor `centered:`/`full_width_images:` options set different styling per form and baked in the assumption that every event header is a full-width, centered image. Centering already comes from the content's own inline `text-align` (the toolbar writes it), so it renders the same in edit and show without the frame forcing it — the option was redundant. Drop both options and the per-form calls; keep the universal block-spacing and dashed-cell tweaks, which aren't per-form. Co-Authored-By: Claude Opus 4.8 (1M context) --- app/helpers/rhino_editor_helper.rb | 13 ++----------- app/views/events/_form.html.erb | 4 ++-- 2 files changed, 4 insertions(+), 13 deletions(-) diff --git a/app/helpers/rhino_editor_helper.rb b/app/helpers/rhino_editor_helper.rb index 50a85bbfab..abfad9bcc5 100644 --- a/app/helpers/rhino_editor_helper.rb +++ b/app/helpers/rhino_editor_helper.rb @@ -1,9 +1,6 @@ module RhinoEditorHelper # custom rhino editor with stimulus controller attached to edit raw source html - # centered / full_width_images make the editor wrap its content in the same prose - # classes the show page uses for this field, so editing previews match the published - # page (WYSIWYG). Pass whatever the destination view applies (see events/show). - def rhino_editor(form, base_attribute_name, label: nil, hint: nil, centered: false, full_width_images: false) + def rhino_editor(form, base_attribute_name, label: nil, hint: nil) object = Draper.undecorate(form.object) rhino_attr = :"rhino_#{base_attribute_name}" field_id = form.field_id(rhino_attr) @@ -78,13 +75,7 @@ def rhino_editor(form, base_attribute_name, label: nil, hint: nil, centered: fal } ) - wrapper_class = [ - "mb-4 prose max-w-none bg-white prose-strong:text-inherit prose-em:text-inherit", - ("rhino-centered" if centered), - ("prose-img:max-w-full prose-img:w-full" if full_width_images) - ].compact.join(" ") - - content_tag(:div, data: { controller: "rhino-source" }, class: wrapper_class) do + content_tag(:div, data: { controller: "rhino-source" }, class: "mb-4 prose max-w-none bg-white prose-strong:text-inherit prose-em:text-inherit") do safe_join([ label_tag, editor, diff --git a/app/views/events/_form.html.erb b/app/views/events/_form.html.erb index ad3c3f771a..a2d7f566d3 100644 --- a/app/views/events/_form.html.erb +++ b/app/views/events/_form.html.erb @@ -549,14 +549,14 @@ <%= f.input :autoshow_title, as: :boolean, label: "Show title on event show page" %>
    - <%= rhino_editor(f, :header, label: "Header content", centered: true, full_width_images: true) %> + <%= rhino_editor(f, :header, label: "Header content") %> <% if f.object.errors[:rhino_header].present? %>
    Field <%= f.object.errors[:rhino_header].first %>
    <% end %>
    -
    <%= rhino_editor(f, :description, label: "Description", full_width_images: true) %>
    +
    <%= rhino_editor(f, :description, label: "Description") %>