Give the homepage teaser a rich text editor

Replace the plain textarea for the homepage_teaser setting with the shared
TipTap rich-text editor, and render the teaser as sanitized HTML in the
portal hero's default layout.

- SettingsAdmin: teaser field now uses RichTextEditor (lazy-loaded, code-split
  like PostEditor); rich fields render in a <div> wrapper instead of <label>.
- HeroElement: text-block lines flagged `html` render sanitized HTML.
- heroLayout: the default-layout teaser line is now an HTML line.
- admin.controller: sanitize homepage_teaser against the body allowlist on save.
- theme.css: collapse the teaser's nested block margins in the hero.

Closes #48

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-11 11:06:58 -05:00
parent 5ccb18e794
commit a2590812e0
5 changed files with 65 additions and 11 deletions

View File

@@ -4,6 +4,7 @@ const settings = require('../../../model/settings/settings.model')
const users = require('../../../model/users/users.model')
const activity = require('../../../model/activity/activity.model')
const botInternalClient = require('../../../utils/botInternalClient')
const { cleanBody } = require('../../../utils/sanitizeHtml')
const log = require('../../../utils/logger')('admin')
@@ -461,6 +462,12 @@ async function updateSettings(req, res) {
) {
return res.status(400).json({ message: 'Invalid player_registration value' })
}
// The homepage teaser is rich text (HTML) from the shared editor — sanitize it
// against the same allowlist as post/wiki bodies so a stored value is safe (the
// client re-sanitizes on render as defense in depth).
if (typeof updates.homepage_teaser === 'string') {
updates.homepage_teaser = cleanBody(updates.homepage_teaser)
}
try {
await settings.setMany(updates, req.user.id)
await activity.log({ req, action: 'settings.update', detail: { keys: Object.keys(updates) } })