Wiki Phase 2: TipTap rich-text editor + inline image uploads

Replaces the raw-HTML textarea in the wiki editor with a TipTap (ProseMirror)
WYSIWYG editor.

- new RichTextEditor component: bold/italic/strike, H2/H3, bullet+ordered
  lists, blockquote, code block, divider, link, inline image, undo/redo
- generalized POST /admin/uploads (reuses the screenshot multer config) →
  { url }; the editor uploads inline images through it
- editor output still passes through the Phase 1 server-side sanitizer on
  save and DOMPurify on render
- lazy-loaded as its own chunk so the public bundle doesn't ship TipTap
  (public ~82kB gzip; editor chunk ~106kB gzip loaded only in admin)
- RTE styling added to theme.css (toolbar, active states, prose content)

Verified: uploads serve as images; H2/H3 + lists + link + inline image
round-trip through the WYSIWYG and render sanitized on the public page.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-27 10:57:27 -05:00
parent b925114923
commit 4a7dbf0085
9 changed files with 1045 additions and 6 deletions

View File

@@ -54,6 +54,8 @@ adminRouter.post(
ctrl.createPost,
)
adminRouter.post('/posts/upload', upload.single('image'), ctrl.uploadImage)
// Generalized upload (rich-text editors). Same multer middleware; returns { url }.
adminRouter.post('/uploads', upload.single('image'), ctrl.uploadFile)
adminRouter.get('/posts/:id', param('id').isInt(), validate, ctrl.getPost)
adminRouter.put('/posts/:id', param('id').isInt(), validate, ctrl.updatePost)
adminRouter.patch(