mirror of
https://github.com/karatektus/pl_mail.git
synced 2026-10-25 10:04:18 +00:00
Two of the five inert buttons in the compose toolbar's placeholder loop now do what they have always claimed to: the smiley opens a real picker, and the image button puts a picture in the body rather than beside it. The other three are left in the loop untouched. Emoji — the full Unicode set, via `emoji-picker-element` in a `ui--dropdown` shaped like the integration picker beside it, so Escape and outside-click come free. Two things the component does by default are not acceptable here and are configured out: its data source is a jsdelivr URL, replaced by emojibase JSON vendored per locale under assets/emoji (so search works in German); and its labels are English, replaced by the same Twig-side i18n object the rest of the window uses. A Playwright assertion pins the rule the whole feature is built around — emoji enter the body when somebody picks one and at no other time, so `:)` and `:smile:` survive typing byte for byte. Noto Color Emoji (OFL) ships with it, COLRv1, as the ten unicode-range woff2 subsets Google Fonts itself serves: 2.0 MB on disk, but a body with three smileys fetches only the ~140 KB slice they live in. Scoped to the picker and the compose body, so the grid matches the phone keyboard people know it by instead of changing shape per operating system. Inline images — the read and send sides already existed (MessagePart carries contentId/isInline; buildEmail() already embeds), so this adds the two missing halves. DraftAttachmentService::attachInline() stores the bytes down the same path as an attachment and describes them differently: inline disposition, a generated content id, isInline — which keeps syncFlag() from putting a paperclip on a mail whose only "attachment" is a picture the reader can already see. InlineImageRewriter is the inverse of resolveCids(): the editor holds the attachment route because a browser cannot render `cid:`, the wire holds `cid:` because a recipient cannot resolve anything else, and `data-cid` is the identity that bridges them. It runs inside DraftPersister::save(), so autosave, send and plainTextBody() all see the same body. Paste and drag-drop go through the same endpoint. Without them a pasted screenshot becomes a data: URI that every later autosave posts back as base64. Refusal messages from DraftAttachmentService were spelled in English in the method that produced them — the one thing a German compose window said in English. They go through the translator now, in all three locales.