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:
@@ -160,6 +160,15 @@ async function uploadImage(req, res) {
|
||||
return res.status(201).json({ image_url: imageUrl })
|
||||
}
|
||||
|
||||
// Generalized upload used by rich-text editors (wiki, etc.). Same multer config
|
||||
// as the screenshot upload; returns a neutral { url }.
|
||||
async function uploadFile(req, res) {
|
||||
if (!req.file) return res.status(400).json({ message: 'No file uploaded' })
|
||||
const url = `/uploads/${req.file.filename}`
|
||||
await activity.log({ req, action: 'upload', detail: { url } })
|
||||
return res.status(201).json({ url })
|
||||
}
|
||||
|
||||
// ── Wiki pages ─────────────────────────────────────────────────────────
|
||||
async function listWiki(req, res) {
|
||||
try {
|
||||
@@ -457,6 +466,7 @@ module.exports = {
|
||||
publishPost,
|
||||
deletePost,
|
||||
uploadImage,
|
||||
uploadFile,
|
||||
listWiki,
|
||||
getWiki,
|
||||
createWiki,
|
||||
|
||||
Reference in New Issue
Block a user