contact messages: visitor panel and composer

The composer is a constrained contenteditable (plain-text paste,
toolbar formatting, upload chips for images/attachments) serialized to
a Markdown subset by richtext.js — the wire format is text and the
server re-renders it, so no user markup ever reaches a page. Uploads go
to /_contact/files with the session token (admins share the component
via /_api/files). ContactPanel shows the thread pushed live over the
activity socket: pagerite.js lazy-loads the bundle on trigger clicks,
routes socket pushes to it, re-registers the session token on every
reconnect (the resync), and dots trigger buttons on unread replies.
This commit is contained in:
LeoVasanko committed 2026-09-26 01:11:14 +00:00
1 parent 776f408743
commit eea89032a2
6 files changed
+669

No files matched your search

+31
View File
@@ -878,6 +878,37 @@ article {
opacity: 1;
}
/* {contact: Label} directive: the button opening the visitor contact
panel (docs/messages.md). A standalone accent button in the article
flow; .has-unread (set by contact-main) marks a new admin reply. */
.contact-trigger {
position: relative;
padding: 0.35em 1.1em;
font: inherit;
color: var(--accent);
background: none;
border: 1px solid var(--accent);
border-radius: 1.2em;
cursor: pointer;
}
.contact-trigger:hover {
color: var(--bg);
background: var(--accent);
}
.contact-trigger.has-unread::after {
content: "";
position: absolute;
top: -0.25em;
right: -0.25em;
width: 0.6em;
height: 0.6em;
border-radius: 50%;
background: var(--accent);
box-shadow: 0 0 0 0.15em var(--bg);
}
.edit-link {
position: absolute;
top: 0.2rem;