contact messages: fix inline formatting loss, attachment uploads

The serializer treated only DIV/P as blocks: a formatting element
directly under the editable root (a bolded segment of an undivided
line is a top-level <b>) became a separate PLAIN block, losing both
the formatting and the paragraph structure. Top-level inline runs now
accumulate into one paragraph, and style-based formatting (Firefox's
font-weight/font-style spans) maps back to markers.

Attachments: the upload allowlist rejected everything but a handful of
types with a console-only 415. The logic inverts — raster images get
derivatives, script carriers (svg/html/js/xml...) are refused, and any
other file stores as a download chip. Upload failures now show in the
composer, and toolbar buttons keep the contenteditable selection on
click (mousedown.prevent).
This commit is contained in:
LeoVasanko committed 2026-09-26 01:31:45 +00:00
1 parent 975ebfe5e7
commit 49bdedd938
4 files changed
+133 -91

No files matched your search

+8 -6
View File
@@ -93,12 +93,14 @@ injection point, fed only by the strict renderer.
`PUT /_contact/files/{name}` (public, like the messaging socket) stores
panel uploads in the content-addressed file store: raster images through
the usual AVIF/WebP/JPEG derivative pipeline, a small allowlist of document
types (pdf/txt/md/csv/zip) as-is. **SVG and HTML are excluded** — script
carriers. Gates today: a session-shaped `x-contact-token` header, a 20 MB
size cap and the type allowlist. Admin uploads from the same composer go
to the admin-gated `/_api/files` instead. Files uploaded but never
referenced by a sent message currently linger (no orphan GC).
the usual AVIF/WebP/JPEG derivative pipeline, **anything else as-is**,
rendering as a download chip in the thread. The one exclusion is script
carriers (svg/html/xhtml/js/xml…) — an attachment URL can always be
opened directly in the site's origin, where such a file would execute.
Gates today: a session-shaped `x-contact-token` header, a 20 MB size cap
and the type blocklist. Admin uploads from the same composer go to the
admin-gated `/_api/files` instead. Files uploaded but never referenced by
a sent message currently linger (no orphan GC).
## Storage