Labels in Mail UI (chips + Actions + sidebar), plain-text Lato, calendar-panel polish
Labels:
- Moved labelling out of the "+label" message slot into Mail: label chips on the
thread subject bar (removable ✕) and before list subjects, and a Labels submenu
in the per-message Actions menu (multi-select checkboxes + "Create new…").
Per-message storage kept; the reading pane writes a thread-union aggregate so the
list (no message ids) can show chips.
- Sidebar LABELS section rewritten: aligned monocolor tag rows, collapsible
(collapsed by default), inline "+" create popover, hover "−" delete with a confirm
popover, and click-to-filter the mail list by label.
- Backend: batch message/thread label reads, thread aggregate, create + delete
(delete unhooks from all mail), and GET /labels/{name}/threads. Core GET /threads
gained an `ids` param to power the label view. Removed the plugin-pages list from
the Mail sidebar.
Plain-text mail now renders in Lato (self-hosted at /fonts, @font-face'd into the
sandboxed message iframe) instead of the monospace <pre> default.
Calendar right-panel (embed mode): restored the "…" view-options menu, pinned the
toolbar + mini-calendar (only the agenda scrolls), plain "All calendars" button
(inline resets), and the event popover now closes on any outside click.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01U72FZiF1W1pb8XxyWBTREA
This commit is contained in:
@@ -83,12 +83,21 @@ async def list_threads(
|
||||
offset: int = Query(0, ge=0),
|
||||
filter: str = Query("all"), # all | unread | pinned
|
||||
order: str = Query("desc"), # desc (newest first) | asc (oldest first)
|
||||
ids: str = Query(""), # comma-separated thread ids (e.g. a plugin's label view)
|
||||
) -> ThreadListOut:
|
||||
# Collect WHERE conditions once so the page query and the total-count query
|
||||
# stay in lockstep (offset pagination needs an accurate grand total).
|
||||
conditions: list[ColumnElement[bool]] = [Thread.user_id == user.id]
|
||||
|
||||
if view.startswith("account:"):
|
||||
if ids:
|
||||
# Explicit id set (ignores view/folder) — used by plugin-driven views like
|
||||
# "all mail with label X", which supply the matching thread ids.
|
||||
try:
|
||||
wanted = [uuid.UUID(x) for x in ids.split(",") if x.strip()]
|
||||
except ValueError:
|
||||
raise_api_error(400, "bad_ids", "invalid thread id in ids")
|
||||
conditions.append(Thread.id.in_(wanted or [uuid.uuid4()]))
|
||||
elif view.startswith("account:"):
|
||||
# A single account's inbox: threads with a message in THIS account's
|
||||
# inbox mailbox. Keeps the per-account view consistent with the unified
|
||||
# Inbox (archived/trashed/sent threads don't leak in).
|
||||
|
||||
Reference in New Issue
Block a user