feat(ui): markdown preview toggle in the composer (LC-276) #316
Loading…
Reference in a new issue
No description provided.
Delete branch "feat/lc-276-markdown-preview"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
What
Adds a Write/Preview toggle to the composer (LC-276). Clicking Preview shows the current draft rendered exactly as it will post; toggling back returns to editing with the draft intact. Pairs with the LC-254 formatting toolbar.
How
POST /room/{id}/preview(routes::room::post_preview) renders the body through the SAME pipeline message posting uses -markdown::render(pulldown-cmark + syntect + custom emoji) - and returns<div class="lc-md leading-snug">...</div>. Access-gated withis_room_accessible(403 otherwise) and body-capped toMAX_MESSAGE_CHARS(LC-153, since the render runs synchronously on the request thread). Mentions are not resolved (the message is unposted), so@userrenders as plain text - this is a formatting preview, not a mention-resolution preview.composer.html: adata-lc-preview-togglebutton in the toolbar + a hiddendata-lc-preview-panein the textarea's flex slot. A small IIFE fetches the render on entering preview (no re-fetch toggling back), swaps textarea<->pane and the button label, and resets to Write after a successful send so no stale preview lingers. The server returns trusted markdown HTML (same trust model as message bodies emitted with|safe).A client-side markdown lib was rejected - it can't match the server's syntect/custom-emoji/sanitization, so the preview would lie. No new DB/env. Not operator-visible.
Tests
routes_drafts:preview_renders_markdown(POSTbody=**bold**-><strong>bold</strong>in anlc-mdwrapper) andpreview_forbidden_when_inaccessible(non-member, private room -> 403).just testandjust test-saaspass.QA note
The toggle is JS-driven (a
fetch+ show/hide), so worth a quick manual pass: type markdown (bold, a list, a fenced code block, a custom:emoji:), hit Preview (renders matching a real message; code is syntect-highlighted), toggle back (draft intact), confirm a successful send returns to Write, and confirm Preview on a room you can't access is gated.Adds a Write/Preview toggle to the composer. Clicking Preview renders the current draft through the SAME server markdown pipeline that posting uses (pulldown-cmark + syntect + custom emoji) and shows it in place of the textarea, so you see exactly what will post before sending; toggling back returns to editing with the draft intact. New POST /room/{id}/preview renders the body via markdown::render and returns the lc-md-wrapped HTML. It is access-gated (403 on a room the caller cannot see) and caps the body to MAX_MESSAGE_CHARS (LC-153, the render is synchronous on the request thread). Mentions are not resolved (the message is unposted), so @user shows as plain text - this is a formatting preview, not a mention-resolution preview. The toggle IIFE fetches once on entering preview (no re-fetch on toggle-back) and resets to Write after a successful send so no stale preview lingers. A client-side markdown library was rejected: it cannot match the server's syntect highlighting / custom emoji / sanitization, so the preview would not match the post. Strings localized in en + es. Tests cover the render output and the access gate. #LC-276 #LC-277 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>