Antispam: "Mark as Spam" belongs in the Actions menu

A button pinned to every message header is a persistent control; marking spam is
a verb you reach for occasionally, like Reply or Delete. It now lives in the
Actions menu, between Labels and Delete.

That needed a slot inside the dropdown, which the catalog had no name for:
`message.actions.menu`. The host wraps it so any click closes the menu, leaving
a bundle to do nothing but act.

The UI kit gains `.wm-menu-item`, @applying the exact utilities ThreadView's own
menu items use — the same trick the sidebar shell already relies on, so a plugin
verb is indistinguishable from a core one rather than approximately similar.

Labels follow the folder as before: "Mark as Spam" in the inbox or archive,
"Mark as not spam" in the spam folder, nothing in Sent/Drafts/Trash. A message
already taught shows that, disabled, instead of offering the lesson again.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018SqJYt7jD7oUEkoY68sBZo
This commit is contained in:
2026-09-17 19:36:29 +02:00
co-authored by Claude Opus 5
parent e420126145
commit c76b186b10
8 changed files with 96 additions and 84 deletions
@@ -145,6 +145,7 @@ const LOCATION_LABEL: Record<string, string> = {
};
function ActionsMenu({
messageId,
onReply,
onPrint,
onDelete,
@@ -154,6 +155,7 @@ function ActionsMenu({
onToggleLabel,
onCreateLabel,
}: {
messageId: string;
onReply: (kind: string) => void;
onPrint: () => void;
onDelete: () => void;
@@ -277,6 +279,12 @@ function ActionsMenu({
)}
</div>
)}
{/* Plugin verbs that belong in the menu rather than on the header
(SPEC §11.3 message.actions.menu). Clicking anything inside closes
the menu, so a bundle only has to perform its action. */}
<div onClick={() => setOpen(false)}>
<PluginSlot name="message.actions.menu" props={{ messageId }} />
</div>
<div className="my-1 border-t border-app-border" />
{item(t("mail.delete"), onDelete)}
</div>
@@ -441,6 +449,7 @@ function MessageItem({
</button>
)}
<ActionsMenu
messageId={message.id}
onReply={onReply}
onPrint={onPrint}
onDelete={onDelete}