Supermail
A Superhuman-style email client - folders, compose, snooze, follow-ups, splits, flows, and filters on top of Gmail or IMAP - brought into Omniscio as a native sidebar feature and shipped from the same in-repo source as the standalone web client at mail.jls.dev. It is a React port of the vendored Supermail UI, not a webview or an embedded plugin.
What it is
A Superhuman-style email client - folders, compose, snooze, follow-ups, splits,
flows, and filters on top of Gmail or IMAP - brought into Omniscio as a
native sidebar feature and shipped from the same in-repo source as the
standalone web client at mail.jls.dev. It is a React port of the vendored
Supermail UI, not a webview or an embedded plugin.
Status (2026-08-10): one-project Supermail. The UI lives at
src/plugins/supermail/ui, the backend lives atsrc/plugins/supermail/backend, and both deploy from this Omniscio repo. The native Omniscio surface is off by default behindsupermailEnabled; the standalone web build deploys tomail.jls.devand uses the same backend atmailback.jls.dev.
Where to find it
How to turn it on
Settings → Connections → Email & Summaries → Supermail → Supermail (sidebar)
(off by default). The setting is supermailEnabled. While off, the sidebar row is
hidden and nothing Supermail-related loads.
A separate toggle, Show unread in Omniscio inbox (supermailInboxEnabled),
controls whether Supermail pushes unread items into Omniscio's unified inbox. It is
gated by the master supermailEnabled toggle.
How it behaves
What the user sees
- A "Supermail" row appears in the Omniscio sidebar (under the Omniscio built-ins group), with a mail icon. The row only shows when the feature is enabled.
- Clicking it opens Supermail full-pane inside Omniscio, with a
Mail | Sessionsswitch: Mail is the mail client; Sessions lists the Claude working sessions started from Supermail (see "Start a session" below). In Mail mode the switch sits in the Omniscio-owned panel header (SupermailPanel); in Sessions mode it sits atop the session list in the sidebar. Its placement is configurable viasupermailHeaderLayout(current/unified/rail, defaultunified). - Pop it out into its own window — right-click the Supermail sidebar row →
Open in new window, or press the Supermail window global hotkey (default
Ctrl+Alt+M) to open Supermail in a separate desktop window from anywhere, even
while Omniscio is in the background. The hotkey registers only while Supermail is
enabled and is rebindable (or disableable) in Settings → Keyboard Shortcuts
(System-wide). Under the hood this is the shared project-window pop-out
(
openProjectWindow('__supermail__')), so the mail-folder sidebar comes along when you have it pinned. In the default overlay mode (sidebar hidden) the mail view fills the whole window, with no empty column beside it. - One email in its own window — opened for you by an agent. Separate from the
whole-client pop-out above, a single THREAD can be popped into its own desktop
window, so an agent can put an email in front of you and you reply to it right
there. Several can sit side by side, and Ctrl+W closes the one you are looking
at (Escape deliberately does not — it belongs to the mail UI, and closing the
window on it would discard a half-typed reply). Re-opening a thread that already
has a window just focuses it, and at most 8 are open at once so an agent loop
cannot carpet your screen. Agents open one with
POST /supermail/threads/<id>/open→ CLI control surface. Details: supermail-thread-window-contract.md. - The first screen is Supermail's own sign-in, which flows through the
Supermail backend over the
agentmc://supermail/authdeep link (the backend's primary scheme;omniscio://supermail/authis accepted as an alias). Omniscio has no user accounts of its own.
Unsubscribe in one click (+ Unsubscribed folder)
Newsletters and marketing mail — any message carrying a List-Unsubscribe header —
show a small Unsubscribe button in the reading-view message header (ordinary mail
shows nothing). One click does three things:
- opens the sender's unsubscribe page (or
mailto:) in the browser; - saves a filter that moves FUTURE mail from that exact sender to a visible "Unsubscribed" folder — out of the inbox but browsable in the sidebar, never buried in All Mail and never deleted;
- moves any mail already in the inbox from that sender to that same folder.
A single humanized toast confirms all three, with Undo that truly restores it: the swept messages move back to your inbox and the just-created filter is removed. It is idempotent — unsubscribing again from the same sender never creates a duplicate filter. Signed out of the Supermail backend, the button still opens the unsubscribe page and says auto-filing needs your account (never a silent no-op). The filter is an ordinary Supermail filter — view or remove it in Settings → Inbox → Filters, and the "Unsubscribed" folder is a normal label in the sidebar.
It matches the sender's exact email address (a "From email" filter field), so unsubscribing
from news@cnn.com can never also catch a different sender like breakingnews@cnn.com. It
never deletes anything (move, not trash), and it fails safe — if anything ever confuses the
filter, mail stays in your inbox (visible), never silently hidden. A fully silent one-click (no
page opens) and a server-side domain block are out of scope (they need the separate Supermail
backend). One known limit: on a mailing list where the "From" is the person who posted (not the
list), it keys on that person — the visible Unsubscribed folder makes such a slip catchable.
Contract: supermail-quick-unsubscribe-contract.md.
Start a session (Sessions tab + from a thread)
Supermail is a session host: you can start real Claude working sessions from it, the same way the Tasks and Agent Email panels do.
- General — the Sessions tab (in the
Mail | Sessionssidebar strip) shows every session started from Supermail, with a "+ New session" button. Opening one swaps the main pane to its chat; the tab shows a live count badge. The tab lists only your email sessions — it does not show the general-project extras (Councils / Running Recipes). - With Ctrl+T — press Ctrl+T anywhere in Supermail (the inbox, a thread, the compose box, or the Sessions tab) to start a new blank session in the Supermail host; it flips you to the Sessions tab so the new chat is visible. (Seeding a session from a specific email is the "From a thread" command below.)
- From a thread — with a conversation open, click the "Start a session" button in the reading-view header (top-right), or run "Start a session from this thread" from the command palette — both do the same thing. A small dialog opens with an optional first instruction (e.g. "Draft a reply" — or leave it blank) and a Start button. Starting spawns a session seeded with the WHOLE thread (every message) as context. The thread rides along as untrusted text (email is attacker-controlled), so it is treated as context to act on, never as instructions to obey. (A session-type picker appears here only when more than one type is available; today there's just one, so the dialog stays this simple.)
- Back to a session, from the email — once you've started session(s) from a thread, its emails show a "N session" chip in the reading-view header; click it to jump straight to the newest session (the Sessions tab still lists them all). The chip shows only on emails whose thread actually spawned a session.
Sessions run in a managed Supermail workspace (nothing touches your real repos unless you tell the session to). Output shows in the session view — nothing is posted back into your mailbox. This is gated by the same Supermail toggle; there is no separate switch. For agents, the mechanics + invariants live in supermail-start-session-contract.md.
Reading mail through your Omniscio Google connection (shared grant)
By default, Supermail reads mail through your Omniscio Google connection — so if you
already connected Google to Omniscio in Settings, you are never asked to sign into Google a
SECOND time inside Supermail. The toggle Use Omniscio's Google connection for mail
(supermailUseAmcGmail, default ON since 2026-08-14 — unified sign-in) controls this:
when it is ON (the default) and Google is connected in Omniscio Settings, Supermail reads
your inbox through Omniscio's ONE shared Google grant — the same grant that backs Gmail /
Calendar / Drive / Sheets / Docs. No second consent screen and no new Google permission (the
shared grant already covers Gmail). Turn it OFF to use Supermail's OWN separate backend
sign-in instead. The default is self-targeting: it only takes effect once Google is
connected in Omniscio, so a user who has not connected Google simply stays on Supermail's own
sign-in — no one is stranded, and one toggle flip reverts it.
- The Google token never leaves the main process. Supermail's mail calls are relayed
through Main (
gmail:api-request), which holds the token and is hard-bound to your own Gmail (gmail.googleapis.com/.../users/me); only mail DATA crosses to the renderer. - Reversible. Turn the toggle OFF to return to Supermail's own sign-in. It also auto-falls-back (to Supermail's backend / demo path) whenever the shared grant is not connected or Supermail runs outside the Omniscio desktop app — so it never leaves a dead inbox; with no other session, Supermail shows its own sign-in screen.
- An expired Google sign-in says so, with a one-click fix. When the shared Google sign-in
has EXPIRED (not merely never connected), Supermail's sign-in screen says "Your Google
sign-in has expired" and, on the computer, offers Reconnect Google — the same
reconnect Omniscio's own Gmail panel runs — which brings you straight back to your inbox. On
a phone it says to reconnect on your computer, because Google's sign-in opens a browser
there. A background label refresh that fails in the meantime just keeps the labels you have
(it is no longer reported as a crash). Invariant:
an-expired-grant-offers-reconnect-google. - A failed send is recoverable in one click. If a send (or other write) can't authorize because the shared Google grant went stale or is missing send permission, Supermail no longer fails silently — a persistent "can't send email — reconnect Google" card appears in your inbox with a one-click Reconnect Gmail button (and the Google Account settings card gains a Reconnect button while connected). It clears itself the moment a send succeeds; an ordinary network / rate-limit / 5xx hiccup never raises it. See inbox-alerts.md.
- Scope. Covers the core inbox (list, read, search, archive, mark-read, label, send
— including choosing which of your Gmail "Send mail as" identities a message goes out
from, via the composer's From picker (read from Gmail settings through the relay; it
quietly falls back to just your primary address if the connection predates the needed
permission — reconnect Google via the sidebar's Reconnect to enable your other
addresses), snooze) — plus viewing and downloading received attachments (inline images render in the
body, image attachments show a thumbnail, and downloads save the real file; the bytes are
fetched from Gmail via the relay) and, via the local mirror, background sync, exact per-lane
split-counts, offline reading, and local search (see the mirror bullet below). Backend-only
extras (multiple mailboxes, drafts, scheduled send, attachments-on-send) stay on Supermail's
own backend and are degraded in this mode. Engineering invariants:
.claude/memory/contracts/supermail-shared-gmail-grant-contract.md. - Works on your phone (mobile web). With this toggle ON, your inbox now loads and is fully usable from the Omniscio mobile web UI (over Tailscale) — read, archive, reply, and send. It reads from the desktop's synced mirror and signs in off the shared grant, so no mailbox key is sent to the phone. For safety a paired phone is bounded to those triage actions: it CANNOT permanently delete mail or change Gmail forwarding/filters (those stay desktop-only, refused by a remote allowlist), and only your approved, signed-in device can reach it. See invariant 4 of the shared-grant contract.
- A send that needs a Google reconnect fails LOUDLY, never silently (F162). In this mode a
reply whose Gmail send returns 401/403 (the shared grant lacks the send scope) surfaces a
persistent, actionable "reconnect your Google account in Settings" toast and restores your
draft — not a vague, disappearing "try again", and never a silently-lost reply. It does NOT tear
the read session down (the inbox keeps working while you re-consent); reconnecting Google in
Settings restores sending. Invariant:
.claude/memory/contracts/supermail-resilience-contract.md(the F162 bullet). - Full inbox, not just the first page. The shared-grant connector pages the inbox with
Gmail's own
nextPageTokencursor (getThreadsPageinshared/gmail-api/gmail-api.ts), so the list loads your WHOLE inbox as you scroll — not a single ~25-thread page. (Before this it fetched one page and dropped the cursor, so a large inbox showed only ~25 mails with no way to load more.) Each listed thread still costs athreads.get, so this direct path is heavier than the backend's server-side mirror; a local mirror for the shared-grant path (instant cold-load, offline reading, background sync, local search, exact counts) landed as the next bullet. Split-tab counts on this path are now exact over the local mirror — tallied from the whole local copy, not just the ~one loaded page (Gmail can't cheaply count arbitrary custom lanes, so the mirror does it), falling back to the loaded-page estimate only before the copy fills. - Local mailbox mirror (instant load + offline + always-on background sync). On this path
the inbox reads from a local per-account copy of your mailbox kept in the app's main
process (a standalone SQLite file), so it paints instantly and works offline — falling back
to the live Gmail connection whenever the copy is cold (never a dead inbox). Main owns the
account (the renderer never names it), the copy is wiped when you sign out of Google (it
holds your mail), and the initial fill runs low-priority + backs off under load, so it never
contends with your machine. A background sync engine keeps the copy fresh even when the
Supermail panel is closed — a light ~5-minute check that pulls only what changed via Gmail's
history.list(from a saved bookmark), applies new/removed mail, and self-heals if the bookmark ever expires; it trims (never fully stops) under machine load and never triggers the big initial download on its own. When a background check lands new mail it now wakes the on-screen inbox to refresh itself — a payload-free nudge from the main process to the panel that re-fires the inbox's normal refresh — so freshly-synced mail appears on its own, instead of waiting for the next time you focus or reopen Mail. And changes you make in Gmail or Superhuman itself — archive, star, mark-read — now surface in seconds, not minutes: on top of the ~5-minute check, the copy also syncs the moment you're about to look at Supermail — when you open or switch to it, when you bring the app back to focus (e.g. returning from your browser), and on a ~60-second loop while you're watching it side-by-side with your browser. On top of that, the open inbox re-reads the local copy on its own about every 30 seconds — so an email you archive (or star / mark read) in Gmail or Superhuman disappears from the list, and the tab counts update with it, on their own — even if you never click into Supermail or switch windows (that periodic self-refresh pauses while Supermail is hidden and reads only the local copy, so it stays free). Every one of those is coalesced (single-flight + a short throttle) and self-gates when the mirror isn't in use, so it stays cheap and never over-fetches. When Supermail is enabled, both the panel and this local copy are warmed a few seconds after the app launches — so even the first time you open Supermail in a session it appears instantly with already-fresh mail, instead of pausing to load and sync (only on the shared-Google path with an account connected; off when Supermail is disabled or "keep panels loaded" is off). Off-switches:AMC_DISABLE_SUPERMAIL_MIRROR_BACKFILL(the initial fill) +AMC_DISABLE_SUPERMAIL_MIRROR_SYNC(the background sync) +AMC_DISABLE_SUPERMAIL_MIRROR_BODY_BACKFILL(offline-body caching) +AMC_DISABLE_SUPERMAIL_MIRROR_SEARCH_INDEX(the local search index). This is Stage 2a (local copy + instant load) + Stage 2b (the always-on background sync) + Stage 2c, which layers three things on top: instant thread open + offline message bodies — opening a thread now paints its body instantly from the local copy (no more ~0.25–0.5s blank while it waits on Gmail), then quietly upgrades to the full live thread with attachments / cc / bcc in the background; those same cached bodies also let a thread open with no connection at all, and the visible inbox rows are preloaded from the local copy so clicking through stays instant (if a message's body isn't cached yet, it just opens the old way rather than flashing an empty email); local search — subject / sender / body full-text search AND the common operators (from:/to:/subject:/is:unread/is:starred) answered instantly from the local copy, falling back to a live Gmail search only for operators it genuinely can't evaluate (e.g.has:attachment, date ranges, other folders), a negated operator, or when you choose "Search all mail"; and exact per-lane split-tab counts over the whole local copy. Like the fill and the sync, the body-caching and search-indexing are paced, back off under machine load, and are each individually off-switchable — they never contend with your machine. Invariants:.claude/memory/contracts/supermail-local-mirror-contract.md.
New-mail notifications (which producer runs, and why it varies)
When mail arrives, the desktop alert comes from one of two producers, chosen by which sign-in you use — never both:
- Reading through your Omniscio Google connection (the default,
supermailUseAmcGmailON) — the alert comes from your local copy of the mailbox, which is already kept fresh by the background sync described above. No Supermail backend session is involved anywhere in the path. - Supermail's own sign-in (the toggle OFF) — the alert comes from Supermail's backend notification stream, exactly as before. Your mail is not kept locally in that mode, so there is nothing local to notify from. Nothing about this path changed.
Why the split exists. The backend stream is keyed to a session token with a 30-day life that nothing renews — and it cannot be renewed without a Google consent screen, so no automation can do it for you. When it lapsed, mail kept syncing perfectly while notifications alone silently stopped, behind a repeating "notifications are disconnected" alert. Reading from the local copy removes that session from the notification path entirely, so there is nothing left to expire. In this mode the backend stream stands down and any standing "disconnected" alert is cleared, because in this mode that alert is no longer true.
Behaviour you can expect:
- Freshness. Alerts arrive on the next sync. The sync runs every 5 minutes, and speeds up to about every 60 seconds while you are actively at the computer (it backs off on its own when the machine is idle or busy). The Settings → Email & Summaries → Notifications preferences — desktop on/off, the Important-only filter, and quiet hours — apply to these alerts.
- Unread mail from other people only. A thread you have already read, and a note you mail yourself, never produce an alert — the same rule the backend stream applies.
- One alert per conversation. Several new replies in one thread produce a single alert, not several.
- A big backlog can never flood you. A burst of new mail alerts on at most a handful of the newest threads and collapses the rest into one silent "N more new emails" summary.
- It is impossible to double-alert. A mail pass that overlaps a previous one (the sync resumes from its boundary point after a large backlog) cannot re-alert the same mail.
- The "Emails in inbox" option keeps working in this mode — each new email still creates its dismissible Omniscio inbox card with the inline reply box.
One honest limitation: the "VIP only" notification filter currently results in no alerts, in this mode and on the backend stream alike — the backend reports VIP as always-false, so the setting is inert today. Engineering invariants: supermail-notification-delivery-contract.md.
Superhuman snooze-reminders are filtered out
If you also use Superhuman, its snooze feature brings a thread back by mailing you a
"Reminder" note from reminder@superhuman.com ("This is a reminder from Superhuman Mail. Do
not reply to this email."). Supermail now treats that reminder as noise — exactly like its
own snooze-return marker: it never becomes the inbox row's sender/preview and never shows
inside the opened thread, so the row keeps the real person and message instead of
"Reminder". The conversation itself stays in the inbox (at its real date, with no
artificial bump); only the reminder message is hidden. Detection is by sender, scoped to
the snooze reminder only (a one-off sharing@superhuman.com note is a different Superhuman
feature and is left alone). Existing reminder-topped threads clean up on the mirror's next
self-healing rebuild. Engineering invariants: the local-mirror contract (thread-we-snoozed-never-evicted) in
supermail-local-mirror-contract.md.
Filters (your own auto-acting rules)
Supermail has its own filter rules, separate from Gmail's filters, that automatically act on incoming mail. Manage them at Settings → Inbox → Filters (a master on/off toggle + a "Manage filters" launcher).
- Start one from a message (
T, right-click, or the funnel button) — press T on a focused inbox thread (or the open conversation), right-click any message row and pick "Filter messages like these", OR click the funnel button in the open email's top-right controls (all three share the sameopenWithSeedseed path), to open a new filter pre-seeded with that sender (From is exactly …— Gmail-style "filter messages like these"); you choose the action and save. The editor opens over the inbox (the manager is mounted app-level and driven byfilter-manager-store), not only from Settings. BareTis global; the seed carries no action so a real rule requires you to finish it. - Apply a new filter to your existing inbox on create. When you start a filter from an email, the
editor offers "Also apply to matching emails already in my inbox" — ON by default for that flow
(OFF for a blank "New filter" from Settings). Creating the filter then sweeps your whole current
inbox with that one new rule (not just future mail), refreshes the list, and reports the count; a
Trash-bearing sweep is confirmed first and the button shows a loading state. This is a manual,
one-shot apply (
runManualOnInboxForRule→ the runner inmanualmode) — it does NOT weaken the no-retroactive-sweep rule for the AUTO path (no-retroactive-sweep). - A rule is "conditions → actions." Match on sender, recipients (to/cc/bcc), subject, full body, sender domain, labels, date ranges, attachments (type/size), message state (unread/starred/muted/snoozed/has-link/calendar), and whether it's a mailing list (the message carries a List-Unsubscribe header) — each text condition can be contains, exact, regex, or fuzzy, combined with AND / OR / NOT.
- Actions: archive, add/remove label, move (= label + archive), mark read/unread, star, mute, snooze until a chosen delay, and mark as spam — all reversible, so they apply automatically. Forward to an address and auto-reply with a Quick Reply also apply automatically, but only behind a safety guard (next bullet). Trash is manual-only — never applied automatically (owner safety decision).
- Auto-forward and auto-reply are guarded. Because they send real email, they never fire on a mailing-list or no-reply message, on a thread you've already replied to, or to yourself; they send at most once per sender within a day and stop at a daily send cap. Like every auto action they never touch your existing inbox on enable, and a "Run on inbox now" that would send email is confirmed first. Note the whole engine runs client-side while the app is open, so auto-send is best-effort during that window, not a 24/7 server responder.
- When they run: automatically on new mail as it syncs in (while the app is open), plus a manual "Run on inbox now" for mail you already have. Enabling filters NEVER retroactively sweeps your existing inbox — only genuinely new mail is auto-acted, and each message is acted on once.
- Where it lives: your rules now live on the Supermail backend (like Split Inbox and
Flows), so they sync across your devices, the
mail.jls.devweb client, and AI agents. Only the matching runs client-side — that is what lets a rule use regex or fuzzy text (which a database can't evaluate). The master on/off toggle stays a per-device local switch. - Manage them from an AI agent / CLI. An agent hitting the AMC control server can list, create,
edit, delete, enable, and reorder your filter rules headlessly, and they sync straight to the app
— see the amc-control
supermail-filtersskill spoke. (Running rules over the inbox from the CLI is a planned fast-follow; for now use the in-app "Run on inbox now".) - Engineering invariants (idempotency, fail-closed deep matching, no-retroactive-sweep, regex safety,
reversible-or-guarded-send auto actions + the send-guard, backend storage + row-level security,
the CLI proxy):
.claude/memory/contracts/supermail-filters-contract.md. Code:src/plugins/supermail/ui/src/features/filters/+ the backendsrc/protected/filters/.
AI Filtering (the AI-powered sibling)
Alongside the rule-based filters above, an in-development AI Filtering feature lets AI triage each new email against a plain-language instruction (keep / archive / label / mark-read / star — never trash), preview-first and off by default, cost-capped, mirroring the Inbox Pilot's evaluator for email. Full details: supermail-ai-filtering.md.
Split Inbox (focused lanes, with an on/off switch)
Supermail can divide the inbox into split lanes (Primary, Other, VIP, Team, Calendar…) shown as tabs above the list. A master on/off switch turns the whole feature off for a single unified inbox, and back on to restore it. It lives in TWO places: Settings → Inbox → Split Inbox → "Split inbox", AND right in the "Manage split inboxes" dialog (the ⚙️ gear next to the tabs) as a "Turn off split inbox" button — because the gear is where users actually look to get rid of the tabs (the tab strip alone only switches lanes). Turning it off from the dialog also closes the dialog so the now-tabless inbox shows immediately.
- Off = zero split inboxes. The list shows ALL inbox mail as one stream, the tab strip (and its settings gear) is hidden, and split scoping is skipped. Your configured splits are not deleted — they stay on the backend and reappear the moment you switch it back on. Re-enable from Settings → Inbox → Split Inbox (the in-inbox gear is gone while off, so Settings is the way back).
- Default OFF — a single unified inbox out of the box; splits are opt-in (owner
decision: a single inbox is the saner default, and the tabs were confusing users into
thinking mail was "missing" when it was only scattered across lanes). Device-local
(browser localStorage
supermail:split-inbox-enabled), like the other display prefs, so it doesn't sync to themail.jls.devweb client. Store:features/split-inbox/split-enabled-store.ts. - GOTCHA (agents): when off, BOTH the render view-key (
inbox-page.tsx) ANDcurrentInboxViewKey()(features/split-inbox/inbox-view-key.ts) must force the plainlabel-inboxview — otherwise a focus/sync refetch pulls a stale split partition and shows a subset as "all mail". The two gates are a pair; the refetch half is locked byinbox-view-key.test.ts("targets the plain inbox when OFF"). - Manage the lanes themselves at Settings → Inbox → Split Inbox → "Manage split inboxes" (add/edit/reorder/remove custom splits; the four Gmail-category builtins + the catch-all are system-managed).
- Manage them from an AI agent / CLI. An agent hitting the AMC control server can list,
create, edit, delete, and reorder your split inboxes headlessly (
/supermail/splits*, a thin main→backend proxy — apply-immediately, global-token-only, the JWT stays in Main), so you can ask an agent to "set up a split inbox for me" — see the omniscio-controlsupermail-splitsskill spoke. Invariants: supermail-splits-cli-contract.md.
Inbox list: the View options menu (sort · group · unread)
A single "View options" menu (a sliders button in the inbox toolbar) holds three device-local list toggles — sort direction, group by sender, and unread only. Each is a stay-open checkbox row, so you can flip several without the menu closing; all are remembered across restarts and default to today's behaviour, so an untouched inbox is unchanged:
Sort direction — flip the list between Newest first (default) and Oldest first. It reorders the mail already loaded in the inbox; more history keeps loading as you scroll, exactly as the inbox fills today (it does not force-pull the whole mailbox). Auto-load-on-scroll stays tied to the natural newest-first flow, so oldest-first can't trigger a runaway "keep loading" loop.
Group by sender — cluster the inbox under a header per sender instead of the date separators. Within each sender the emails stay date-ordered (the sort direction controls that order too), and every row still shows its own date — so you see sender AND date ("sort by sender, then by date"). Off by default (the flat date-grouped list). Grouping keys on the sender's email, so two different people who share a display name stay in separate groups.
Unread only — the same one-click "show only unread" filter that used to be a standalone toolbar button, now the third row of the menu (
inbox-store.listFilter). While it is on, the list pages do not auto-open the top row in the reading pane — you pick the thread. Opening a thread marks it read, so an unattended pick would drop that row straight back out of the filtered list and take the next one with it: the list read itself away and the panel crashed on React's nested-update limit ("Something went wrong"). The guard isreadingPaneConsumesFilterinfeatures/inbox/inbox-filters.ts, honoured by bothinbox-page.tsxandlabels/label-page.tsx; every other quick-filter (starred / important / no-reply) is inert to reading a thread and keeps its auto-select.
Sort + group are also command-palette actions (Ctrl/Cmd+K → "Inbox: Toggle sort
direction" / "Inbox: Toggle group by sender"); no dedicated single-key shortcut in v1.
Device-local (supermail-inbox-view-prefs), like the other display prefs, so they don't
sync to the mail.jls.dev web client. Sort/group are a display-only transform — the
inbox store's canonical order and its pagination are untouched — in
features/inbox/inbox-view-order.ts + features/inbox/inbox-view-prefs-store.ts, with
the menu built on the shared Dropdown in features/inbox/inbox-view-menu.tsx and the
sender-aware list headers in features/inbox/thread-list.tsx.
For agents
Settings: seven tabs, not one long page
Supermail's own Settings (the /settings route inside Supermail — reached from the nav,
or the Settings command in the palette) is grouped into tabs rather than the single
sixteen-section scroll it used to be. Every path in this library that reads
Settings → <em>tab</em> → <em>section</em> refers to this page, NOT Omniscio's main Settings.
| Tab | What lives there |
|---|---|
| General | Personalization (view presets, sidebar / reading pane / contact pane, hover quick actions, reading layout, replay the keyboard tour) and Confirmations (Undo toast, blank-subject warning) |
| Reading | Toolbar buttons (the three-way Shown / Menus only / Hidden control) and Triage Actions |
| Inbox | Split Inbox, Filters, AI Filtering (when revealed), Archive Insights, Contact groups |
| Composing | Sending (default send behavior + shortcut rebinding) and Snippets |
| Keyboard | Left-handed navigation, hover-to-act |
| Notifications | Desktop notifications + quiet hours, Blocked domains |
| Account | Sign out, and the Danger zone (reset account data) |
Two things worth knowing:
- Only the open tab is mounted. Sections on other tabs are genuinely absent from the DOM, not hidden with CSS — so a test or a script looking for a setting must open its tab first, and opening Settings no longer wires up every store on the page.
- The tab does not persist. Settings always opens on General; the active tab is view state, not a saved preference. Escape still returns you to wherever you opened Settings from.
The strip is a real WAI-ARIA tablist: arrow keys move between tabs (wrapping at both ends), Home / End jump to the first and last, and only the selected tab is in the tab order.
Related
- Supermail (part 2) — the continuation of this page.
- Supermail (part 3) — the continuation of this page.
Each Supermail feature has its own page: AI Filtering, Catch me up, Assistants, Archive Insights, the AI Digest, Contact Groups, send shortcuts and the reading view.
Last verified 2026-10-06