build the app, and ship it on Linux and Windows as well as macOS

The client itself: the sync engine and mirror, the Gmail and IMAP providers,
the screener, the three boxes and two piles, compose and send, the sanitiser
and tracker stripping, contacts, clips, backup, notifications and the guide.

The pipeline that ships it. Three runners now build in parallel and the
publish gate wants all four platform keys in latest.json before a release
leaves draft. Linux gets two more packages Tauri does not build: a flatpak
repackaged from the deb against GNOME 48, and a Nix package that relinks the
deb against nixpkgs so it runs as a native Wayland client rather than through
Xwayland. CI runs the Rust suite on all three desktops rather than on Linux
alone, and rebuilds the flatpak manifest on every push to main.

Two things that were only ever exercised on macOS and were wrong everywhere
else. The menu bar was built by adjusting the submenus macOS is given, so on
a platform whose default menu has no File or View the new File menu landed
after Edit, Check for Updates landed nowhere, and the three places and the
reading pane had no menu at all. The deb declared libwebkit2gtk-4.1-0 and
libgtk-3-0 twice, because tauri.conf.json named the dependencies Tauri
already emits.

The updater key exists now, so releases can sign their artifacts.
This commit is contained in:
pj committed 2026-09-06 12:15:30 +05:30
1 parent 088ec9c6e4
commit eb59cb5f8d
423 files changed
+93217 -251

No files matched your search

+128
View File
@@ -0,0 +1,128 @@
<!doctype html>
<html lang="en" data-theme="light">
<head>
<meta charset="utf-8">
<title>Margin Mail: Connect</title>
<link rel="stylesheet" href="mail.css">
<style>
/* The welcome screen is the only place in the app that is not a list, a pane or an overlay, so
these are its own pieces. Every value is a token. */
.welcome {
margin: auto;
padding: 0 24px 56px;
display: flex;
flex-direction: column;
align-items: center;
text-align: center;
}
.welcome-mark {
margin-bottom: 24px;
}
.welcome-title {
margin: 0;
font-family: var(--font-heading);
font-weight: 500;
font-size: 32px;
letter-spacing: -0.02em;
}
.welcome-line {
margin: 9px 0 28px;
max-width: 30em;
color: var(--ink-soft);
font-size: var(--t-4);
}
/* One field and one button, which is the whole of the first step. The button is larger than a
button anywhere else in the app for the same reason it is the only one here. */
.imap-address {
display: flex;
flex-direction: column;
align-items: center;
gap: 14px;
width: 100%;
max-width: 24em;
text-align: left;
}
.imap-address .field {
width: 100%;
display: flex;
flex-direction: column;
gap: 6px;
}
.field-label {
color: var(--ink-faint);
font-size: var(--t-1);
font-weight: 600;
letter-spacing: 0.08em;
text-transform: uppercase;
}
.field-input {
width: 100%;
padding: 9px 12px;
border: 1px solid var(--line-strong);
border-radius: var(--r-sm);
background: var(--raised);
font: inherit;
font-size: var(--t-4);
color: var(--ink);
}
.imap-address .button {
min-height: 38px;
padding: 0 20px;
border-radius: var(--r-md);
font-size: var(--t-4);
}
.welcome-privacy {
margin: 22px 0 0;
max-width: 42em;
color: var(--ink-faint);
font-size: var(--t-2);
line-height: 1.6;
}
</style>
</head>
<body>
<div class="app">
<header class="titlebar">
<div class="traffic"><i></i><i></i><i></i></div>
<div class="lead"></div>
<div></div>
<div class="trail"></div>
</header>
<div class="stage">
<div class="welcome">
<div class="welcome-mark">
<svg width="54" height="54" viewBox="0 0 54 54" fill="none">
<rect width="54" height="54" rx="13" style="fill: var(--accent)"/>
<rect x="14" y="18.5" width="26" height="17" rx="3.2" style="stroke: var(--accent-contrast)" stroke-width="2"/>
<path d="m14.8 20.5 10.5 7.6a3 3 0 0 0 3.4 0l10.5-7.6" style="stroke: var(--accent-contrast)" stroke-width="2" stroke-linecap="round"/>
</svg>
</div>
<h1 class="welcome-title">Margin Mail</h1>
<p class="welcome-line">A quiet, keyboard-first client for your mail, where every decision you make stays on your own machine.</p>
<form class="imap-address">
<div class="field">
<label class="field-label" for="address">Email address</label>
<input class="field-input" id="address" type="email" value="[email protected]">
</div>
<button class="button" data-variant="primary" type="button">Continue</button>
</form>
<p class="welcome-privacy">Any mailbox works here: Google, Fastmail, iCloud, Yahoo, Proton through Bridge, a mailbox where you work, or anything else that speaks IMAP. Margin works out the servers from the address, and nothing is stored until the account is added.</p>
</div>
</div>
</div>
</body>
</html>
+294
View File
@@ -0,0 +1,294 @@
<!doctype html>
<html lang="en" data-theme="light">
<head>
<meta charset="utf-8">
<title>Margin Mail: First run</title>
<link rel="stylesheet" href="mail.css">
<style>
/* The first-run panel. Everything else on this page is the Inbox as it already is. */
.onboard-lead {
margin: 0;
font-size: var(--t-4);
line-height: 1.5;
}
.onboard-sub {
margin: 0;
color: var(--ink-soft);
line-height: 1.5;
}
.field .onboard-sub {
margin: -1px 0 3px;
}
.choice {
display: flex;
align-items: center;
gap: 6px;
}
.choice-label {
margin-right: 2px;
color: var(--ink-faint);
font-size: var(--t-2);
}
.choice-option {
padding: 4px 11px;
border: 1px solid var(--line-strong);
border-radius: var(--r-pill);
background: var(--raised);
color: var(--ink-soft);
font-size: var(--t-2);
transition: background 120ms var(--ease);
}
.choice-option[data-on] {
border-color: var(--accent);
background: var(--accent);
color: var(--accent-contrast);
}
</style>
</head>
<body>
<svg width="0" height="0" style="position:absolute">
<defs>
<symbol id="i-search" viewBox="0 0 24 24"><circle cx="11" cy="11" r="7"/><path d="m20 20-3.5-3.5"/></symbol>
<symbol id="i-places" viewBox="0 0 24 24"><path d="M4 6h16M4 12h16M4 18h10"/></symbol>
<symbol id="i-pen" viewBox="0 0 24 24"><path d="M12 20h9"/><path d="M16.5 3.5a2.1 2.1 0 0 1 3 3L7 19l-4 1 1-4Z"/></symbol>
<symbol id="i-chev" viewBox="0 0 24 24"><path d="m6 9 6 6 6-6"/></symbol>
<symbol id="i-reply" viewBox="0 0 24 24"><path d="M9 17 4 12l5-5"/><path d="M20 18v-2a4 4 0 0 0-4-4H4"/></symbol>
<symbol id="i-later" viewBox="0 0 24 24"><circle cx="12" cy="12" r="9"/><path d="M12 7v5l3 2"/></symbol>
<symbol id="i-aside" viewBox="0 0 24 24"><path d="M12 17v4"/><path d="M8 3h8l-1 7 3 3H6l3-3z"/></symbol>
<symbol id="i-snooze" viewBox="0 0 24 24"><path d="M5 3 2 6"/><path d="m22 6-3-3"/><circle cx="12" cy="13" r="8"/><path d="M12 9v4l2 2"/></symbol>
<symbol id="i-archive" viewBox="0 0 24 24"><rect x="3" y="4" width="18" height="4" rx="1"/><path d="M5 8v11a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1V8"/><path d="M10 12h4"/></symbol>
<symbol id="i-more" viewBox="0 0 24 24"><circle cx="5" cy="12" r="1.2"/><circle cx="12" cy="12" r="1.2"/><circle cx="19" cy="12" r="1.2"/></symbol>
<symbol id="i-shield" viewBox="0 0 24 24"><path d="M12 3 4 6v6c0 5 3.5 8 8 9 4.5-1 8-4 8-9V6z"/><path d="m9 12 2 2 4-4"/></symbol>
<symbol id="i-clip" viewBox="0 0 24 24"><path d="m21 11-8.5 8.5a5 5 0 0 1-7-7L14 4a3.3 3.3 0 0 1 4.7 4.7L10.5 17a1.7 1.7 0 0 1-2.4-2.4L16 6.7"/></symbol>
<symbol id="i-note" viewBox="0 0 24 24"><path d="M14 3H6a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V9z"/><path d="M14 3v6h6"/></symbol>
<symbol id="i-bolt" viewBox="0 0 24 24"><path d="M13 2 4 14h7l-1 8 9-12h-7z"/></symbol>
</defs>
</svg>
<div class="app">
<header class="titlebar">
<div class="traffic"><i></i><i></i><i></i></div>
<div class="lead">
<button class="account-chip">
<span class="avatar" data-hue="4">PJ</span>
[email protected]
<svg class="chev" width="12" height="12" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><use href="#i-chev"/></svg>
</button>
</div>
<div class="view-switch">
<button class="view-option" data-active="true"><span class="key">1</span>Inbox</button>
<button class="view-option"><span class="key">2</span>Feed</button>
<button class="view-option"><span class="key">3</span>Paper Trail</button>
</div>
<div class="trail">
<button class="icon-button" title="Search /"><svg width="16" height="16" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><use href="#i-search"/></svg></button>
<button class="icon-button" title="Places ⌘K"><svg width="16" height="16" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><use href="#i-places"/></svg></button>
<button class="write-button"><svg width="14" height="14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><use href="#i-pen"/></svg>Write<span class="key">c</span></button>
</div>
</header>
<div class="stage">
<section class="list-col">
<div class="list-head">
<h1 class="list-title">Inbox</h1>
</div>
<div class="list">
<div class="section">New for you</div>
<div class="row" data-new>
<div class="row-gutter"><span class="row-dot"></span></div>
<span class="avatar" data-hue="2">MR</span>
<div class="row-main">
<div class="row-top"><span class="row-sender">Maya Raghunathan</span><span class="row-count">3</span><span class="row-time">11:42</span></div>
<div class="row-bottom"><span class="row-subject">Dinner on Thursday?</span><span class="row-snippet">Priya said the place on Church Street takes bookings now, want me to</span></div>
</div>
</div>
<div class="row" data-new data-selected>
<div class="row-gutter"><span class="row-dot"></span></div>
<span class="avatar" data-hue="6">AK</span>
<div class="row-main">
<div class="row-top"><span class="row-sender">Arun Kulkarni</span><span class="row-time">10:15</span></div>
<div class="row-bottom"><span class="row-subject">Re: Lease renewal for the studio</span><span class="row-snippet">Attached the revised draft. The only change is clause 7, which now</span></div>
</div>
</div>
<div class="row" data-new>
<div class="row-gutter"><span class="row-dot"></span></div>
<span class="avatar" data-brand>Ai</span>
<div class="row-main">
<div class="row-top"><span class="row-sender">Airbnb</span><span class="row-time">09:03</span></div>
<div class="row-bottom"><span class="row-subject">Your reservation in Lisbon is confirmed</span><span class="row-snippet">Check-in Friday 12 September after 15:00. Your host Inês will send</span></div>
</div>
</div>
<div class="row" data-new>
<div class="row-gutter"><span class="row-dot"></span></div>
<span class="avatar" data-hue="7">SO</span>
<div class="row-main">
<div class="row-top"><span class="row-sender">Sam Okafor</span><span class="row-time">Yesterday</span></div>
<div class="row-bottom"><span class="row-subject">Piano lessons, the form you sent</span><span class="row-snippet">Got it, thank you. Wednesdays at five work for us. Is there a</span></div>
</div>
</div>
<div class="seen">
<div class="section">Previously seen</div>
<div class="row">
<div class="row-gutter"></div>
<span class="avatar" data-hue="5">LB</span>
<div class="row-main">
<div class="row-top"><span class="row-sender">Lena Brandt</span><span class="row-count">7</span><span class="row-time">Yesterday</span></div>
<div class="row-bottom"><span class="row-subject">Kitchen bench quote</span><span class="row-snippet">Sounds good, Julie. Any afternoon next week works for me.</span></div>
</div>
</div>
<div class="row">
<div class="row-gutter"></div>
<span class="avatar" data-hue="3">DP</span>
<div class="row-main">
<div class="row-top"><span class="row-sender">Dev Patel</span><span class="row-time">Mon</span></div>
<div class="row-bottom"><span class="row-subject">Slides from the talk</span><span class="row-snippet">Here they are, plus the reading list I mentioned. The paper on</span></div>
</div>
</div>
<div class="row">
<div class="row-gutter"></div>
<span class="avatar" data-brand>Ds</span>
<div class="row-main">
<div class="row-top"><span class="row-sender">DocuSign</span><span class="row-time">Mon</span></div>
<div class="row-bottom"><span class="row-subject">Completed: Studio lease 2026</span><span class="row-snippet">All parties have completed the envelope. You can access the</span></div>
</div>
</div>
<div class="row">
<div class="row-gutter"></div>
<span class="avatar" data-hue="8">HW</span>
<div class="row-main">
<div class="row-top"><span class="row-sender">Hannah Weiss</span><span class="row-count">2</span><span class="row-time">Sun</span></div>
<div class="row-bottom"><span class="row-subject">Photos from the Hawaii trip</span><span class="row-snippet">Finally went through them all. The ones from the ridge walk are</span></div>
</div>
</div>
<div class="row">
<div class="row-gutter"></div>
<span class="avatar" data-hue="1">RY</span>
<div class="row-main">
<div class="row-top"><span class="row-sender">Russell Young</span><span class="row-time">30 Aug</span></div>
<div class="row-bottom"><span class="row-subject">Pumpkin bread recipe</span><span class="row-snippet">From my mother's card, transcribed as best I could. Bake at 175</span></div>
</div>
</div>
</div>
</div>
<div class="piles">
<button class="pile" data-empty>
<div class="pile-card" data-depth="0">
<div class="pile-top"><svg width="11" height="11" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><use href="#i-later"/></svg>Reply later<span class="key">4</span></div>
<div class="pile-subject">Nothing yet</div>
</div>
</button>
<button class="pile" data-empty>
<div class="pile-card" data-depth="0">
<div class="pile-top"><svg width="11" height="11" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><use href="#i-aside"/></svg>Set aside<span class="key">5</span></div>
<div class="pile-subject">Nothing yet</div>
</div>
</button>
</div>
</section>
<section class="pane">
<div class="pane-bar">
<button class="verb"><svg width="14" height="14" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><use href="#i-reply"/></svg>Reply<span class="key">r</span></button>
<button class="verb"><svg width="14" height="14" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><use href="#i-later"/></svg>Reply later<span class="key">l</span></button>
<button class="verb"><svg width="14" height="14" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><use href="#i-aside"/></svg>Set aside<span class="key">s</span></button>
<button class="verb"><svg width="14" height="14" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><use href="#i-snooze"/></svg>Snooze<span class="key">b</span></button>
<span class="spacer"></span>
<button class="verb"><svg width="14" height="14" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><use href="#i-archive"/></svg>Archive<span class="key">e</span></button>
<button class="icon-button" title="More ."><svg width="16" height="16" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><use href="#i-more"/></svg></button>
</div>
<div class="thread">
<div class="thread-inner">
<div class="thread-head">
<h1 class="thread-subject">Lease renewal for the studio</h1>
<div class="thread-meta">
<span class="avatars"><span class="avatar" data-hue="6">AK</span><span class="avatar" data-hue="4">PJ</span></span>
<span>Arun Kulkarni and you</span>
<span>·</span>
<span>2 messages</span>
</div>
</div>
<div class="banner">
<svg width="14" height="14" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><use href="#i-shield"/></svg>
<span>Blocked <b>1 tracker</b> from HubSpot. Remote images are off for this sender.</span>
<button class="banner-action">Show images</button>
</div>
<div class="msg" data-collapsed>
<div class="msg-head">
<span class="avatar" data-hue="4">PJ</span>
<div class="msg-who"><div class="msg-name">You</div><div class="msg-preview">Hi Arun, thanks for sending the renewal over. Two things before I sign: the notice period in clause 7 and</div></div>
<div class="msg-time">Mon 14:20</div>
</div>
</div>
<div class="msg">
<div class="msg-head">
<span class="avatar" data-hue="6">AK</span>
<div class="msg-who"><div class="msg-name">Arun Kulkarni<span class="addr">[email protected]</span></div><div class="msg-to">to you</div></div>
<div class="msg-time">Today 10:15</div>
</div>
<div class="msg-body">
<p>Hi Priyanshu,</p>
<p>Attached the revised draft. The only change is clause 7, which now reads three months either side rather than six. The rent review in clause 12 stays as it was, tied to the index and capped at four percent.</p>
<p>If that works, sign when you get a moment and I will countersign the same day. Happy to walk through anything on a call, Thursday afternoon is open.</p>
<p>Best,<br>Arun</p>
<span class="quoted">··· Show quoted text</span>
<div class="attachments">
<div class="attachment"><span class="ext">PDF</span>Studio-lease-2026-v2.pdf<span class="size">412 KB</span></div>
</div>
</div>
</div>
</div>
</div>
</section>
</div>
<div class="overlay">
<div class="panel">
<div class="panel-head">
<h2>You are set up</h2>
</div>
<div class="panel-body">
<p class="onboard-lead"><b>1,284 senders</b> were screened in already, because you have written to them or they are in your contacts: 96 to the Inbox, 71 to the Feed and 1,117 to the Paper Trail.</p>
<p class="onboard-sub">From here on, anyone new waits in the Screener until you say where their mail goes. Nobody is told either way.</p>
<div class="field">
<span class="field-label">Start fresh</span>
<p class="onboard-sub">Optional. Mark older mail as seen, so New for you holds only what is recent. Reversible for seven days.</p>
<div class="choice">
<span class="choice-label">Older than</span>
<button class="choice-option">a day</button>
<button class="choice-option" data-on>a week</button>
<button class="choice-option">a month</button>
<button class="choice-option">three months</button>
</div>
</div>
</div>
<div class="panel-foot">
<button class="button">Start fresh</button>
<button class="button" data-variant="primary">Done</button>
</div>
</div>
</div>
</div>
</body>
</html>
+1 -1
View File
@@ -105,7 +105,7 @@
</div>
</div>
<p class="screen-note">Screened-out mail is kept for 90 days under Screened out, then deleted. Change your mind from a sender's contact card at any time.</p>
<p class="screen-note">Screened-out mail sits under Screened out for as long as this account keeps mail on the device. Change your mind from a sender's contact card at any time.</p>
</div>
</div>
</section>
+371
View File
@@ -0,0 +1,371 @@
<!doctype html>
<html lang="en" data-theme="light">
<head>
<meta charset="utf-8">
<title>Margin Mail: Settings</title>
<link rel="stylesheet" href="mail.css">
<style>
/* Settings is a place, so it takes the stage: a rail of sections on the left, one section on the
right. These are its own pieces; every value is a token. */
.settings {
flex: 1;
min-width: 0;
display: flex;
}
.settings-rail {
flex: none;
width: 210px;
display: flex;
flex-direction: column;
gap: 1px;
padding: 12px 10px;
border-right: 1px solid var(--line);
background: var(--sidebar);
}
.settings-rail h1 {
margin: 4px 8px 10px;
font-family: var(--font-heading);
font-weight: 500;
font-size: 17px;
letter-spacing: -0.01em;
}
.settings-tab {
padding: 7px 10px;
border-radius: var(--r-sm);
color: var(--ink-soft);
font-size: var(--t-3);
text-align: left;
transition: background 120ms var(--ease), color 120ms var(--ease);
}
.settings-tab:hover {
background: var(--accent-wash);
}
.settings-tab[data-active] {
background: var(--accent-wash);
color: var(--ink);
font-weight: 600;
}
.settings-panel {
flex: 1;
min-width: 0;
overflow-y: auto;
padding: 24px 36px 40px;
}
.settings-inner {
max-width: 640px;
}
.settings-title {
margin: 0;
font-family: var(--font-heading);
font-weight: 500;
font-size: 22px;
letter-spacing: -0.01em;
}
.settings-note {
margin: 6px 0 20px;
color: var(--ink-soft);
}
.acct {
margin-bottom: 12px;
padding: 14px 16px;
border: 1px solid var(--line);
border-radius: var(--r-lg);
background: var(--raised);
}
.acct-head {
display: grid;
grid-template-columns: 36px 1fr auto;
gap: 12px;
align-items: center;
}
.acct-head .avatar {
width: 36px;
height: 36px;
font-size: 12px;
}
.acct-name {
font-weight: 600;
font-size: var(--t-4);
}
.acct-addr {
color: var(--ink-faint);
font-size: var(--t-2);
}
.swatches {
display: flex;
gap: 6px;
}
.swatch {
width: 15px;
height: 15px;
border-radius: var(--r-pill);
}
.swatch[data-hue="1"] { background: var(--hue-1); }
.swatch[data-hue="2"] { background: var(--hue-2); }
.swatch[data-hue="3"] { background: var(--hue-3); }
.swatch[data-hue="4"] { background: var(--hue-4); }
.swatch[data-hue="5"] { background: var(--hue-5); }
.swatch[data-hue="6"] { background: var(--hue-6); }
.swatch[data-hue="7"] { background: var(--hue-7); }
.swatch[data-hue="8"] { background: var(--hue-8); }
.swatch[data-on] {
box-shadow: 0 0 0 2px var(--raised), 0 0 0 3px var(--ink-soft);
}
.set-rows {
display: flex;
flex-direction: column;
gap: 7px;
margin-top: 12px;
padding-top: 11px;
border-top: 1px solid var(--line);
}
.set-row {
display: flex;
align-items: center;
gap: 10px;
font-size: var(--t-2);
}
.set-row .lab {
flex: none;
width: 84px;
color: var(--ink-faint);
}
.set-row .val {
flex: 1;
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.scopes {
margin-top: 12px;
padding-top: 6px;
border-top: 1px solid var(--line);
}
.scopes .section {
padding: 4px 0 4px;
}
.scope {
display: flex;
align-items: center;
gap: 9px;
min-height: 26px;
font-size: var(--t-2);
}
.scope .tick {
flex: none;
color: var(--ink-faint);
}
.scope .what {
flex: 1;
min-width: 0;
}
.scope .state {
color: var(--ink-faint);
}
.scope[data-missing] .what {
color: var(--ink);
}
.scope-why {
margin: 1px 0 5px 25px;
color: var(--ink-faint);
font-size: var(--t-2);
}
.acct-foot {
display: flex;
gap: 8px;
margin-top: 12px;
}
.settings-quiet {
margin: 18px 0 0;
color: var(--ink-faint);
font-size: var(--t-2);
}
</style>
</head>
<body>
<svg width="0" height="0" style="position:absolute">
<defs>
<symbol id="i-search" viewBox="0 0 24 24"><circle cx="11" cy="11" r="7"/><path d="m20 20-3.5-3.5"/></symbol>
<symbol id="i-places" viewBox="0 0 24 24"><path d="M4 6h16M4 12h16M4 18h10"/></symbol>
<symbol id="i-pen" viewBox="0 0 24 24"><path d="M12 20h9"/><path d="M16.5 3.5a2.1 2.1 0 0 1 3 3L7 19l-4 1 1-4Z"/></symbol>
<symbol id="i-chev" viewBox="0 0 24 24"><path d="m6 9 6 6 6-6"/></symbol>
<symbol id="i-check" viewBox="0 0 24 24"><path d="m5 12 5 5L19 7"/></symbol>
<symbol id="i-minus" viewBox="0 0 24 24"><path d="M6 12h12"/></symbol>
<symbol id="i-plus" viewBox="0 0 24 24"><path d="M12 6v12M6 12h12"/></symbol>
</defs>
</svg>
<div class="app">
<header class="titlebar">
<div class="traffic"><i></i><i></i><i></i></div>
<div class="lead">
<button class="account-chip">
<span class="avatar" data-hue="4">PJ</span>
[email protected]
<svg class="chev" width="12" height="12" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><use href="#i-chev"/></svg>
</button>
</div>
<div class="view-switch">
<button class="view-option"><span class="key">1</span>Inbox</button>
<button class="view-option"><span class="key">2</span>Feed</button>
<button class="view-option"><span class="key">3</span>Paper Trail</button>
</div>
<div class="trail">
<button class="icon-button" title="Search /"><svg width="16" height="16" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><use href="#i-search"/></svg></button>
<button class="icon-button" title="Places ⌘K"><svg width="16" height="16" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><use href="#i-places"/></svg></button>
<button class="write-button"><svg width="14" height="14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><use href="#i-pen"/></svg>Write<span class="key">c</span></button>
</div>
</header>
<div class="stage">
<section class="settings">
<nav class="settings-rail">
<h1>Settings</h1>
<button class="settings-tab" data-active>Accounts</button>
<button class="settings-tab">Appearance</button>
<button class="settings-tab">Mail</button>
<button class="settings-tab">Privacy</button>
<button class="settings-tab">Screener</button>
<button class="settings-tab">Piles and snooze</button>
<button class="settings-tab">Writing</button>
<button class="settings-tab">Notifications</button>
<button class="settings-tab">Keyboard</button>
<button class="settings-tab">Backup</button>
<button class="settings-tab">Data</button>
<button class="settings-tab">About</button>
</nav>
<div class="settings-panel">
<div class="settings-inner">
<h2 class="settings-title">Accounts</h2>
<p class="settings-note">Two accounts, each with its own places, rules and piles. The colour is the edge on a row in All accounts.</p>
<div class="acct">
<div class="acct-head">
<span class="avatar" data-hue="4">PJ</span>
<div>
<div class="acct-name">Priyanshu Jain</div>
<div class="acct-addr">[email protected]</div>
</div>
<div class="swatches">
<span class="swatch" data-hue="1"></span>
<span class="swatch" data-hue="2"></span>
<span class="swatch" data-hue="3"></span>
<span class="swatch" data-hue="4" data-on></span>
<span class="swatch" data-hue="5"></span>
<span class="swatch" data-hue="6"></span>
<span class="swatch" data-hue="7"></span>
<span class="swatch" data-hue="8"></span>
</div>
</div>
<div class="set-rows">
<div class="set-row"><span class="lab">Signature</span><span class="val">Priyanshu Jain · 73ai</span></div>
<div class="set-row"><span class="lab">Aliases</span><span class="val">[email protected], [email protected]</span></div>
</div>
<div class="scopes">
<div class="section">Permissions</div>
<div class="scope">
<svg class="tick" width="13" height="13" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"><use href="#i-check"/></svg>
<span class="what">Read and change your mail</span><span class="state">Granted</span>
</div>
<div class="scope">
<svg class="tick" width="13" height="13" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"><use href="#i-check"/></svg>
<span class="what">Read your signature and aliases</span><span class="state">Granted</span>
</div>
<div class="scope">
<svg class="tick" width="13" height="13" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"><use href="#i-check"/></svg>
<span class="what">Read your contacts</span><span class="state">Granted</span>
</div>
<div class="scope">
<svg class="tick" width="13" height="13" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"><use href="#i-check"/></svg>
<span class="what">Read the people you have written to</span><span class="state">Granted</span>
</div>
<div class="scope">
<svg class="tick" width="13" height="13" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"><use href="#i-check"/></svg>
<span class="what">Keep a backup in your Drive</span><span class="state">Granted</span>
</div>
<div class="scope" data-missing>
<svg class="tick" width="13" height="13" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"><use href="#i-minus"/></svg>
<span class="what">Answer calendar invitations</span>
<button class="button">Grant</button>
</div>
<p class="scope-why">Calendar is not connected, so an invitation renders but Accept, Maybe and Decline do nothing. Granting reopens the Google consent page.</p>
</div>
</div>
<div class="acct">
<div class="acct-head">
<span class="avatar" data-hue="2">PS</span>
<div>
<div class="acct-name">Personal</div>
<div class="acct-addr">[email protected]</div>
</div>
<div class="swatches">
<span class="swatch" data-hue="1"></span>
<span class="swatch" data-hue="2" data-on></span>
<span class="swatch" data-hue="3"></span>
<span class="swatch" data-hue="4"></span>
<span class="swatch" data-hue="5"></span>
<span class="swatch" data-hue="6"></span>
<span class="swatch" data-hue="7"></span>
<span class="swatch" data-hue="8"></span>
</div>
</div>
<div class="set-rows">
<div class="set-row"><span class="lab">Signature</span><span class="val">Not set</span></div>
<div class="set-row"><span class="lab">Aliases</span><span class="val">None on the provider</span></div>
<div class="set-row"><span class="lab">Permissions</span><span class="val">All granted</span></div>
</div>
</div>
<div class="acct-foot">
<button class="button"><svg width="13" height="13" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><use href="#i-plus"/></svg>Add account</button>
<button class="button" data-variant="ghost">Remove an account</button>
</div>
<p class="settings-quiet">Margin Mail, Margin and Margin Calendar share one Google client, so your Google account lists them once, as Margin, and revoking it revokes all three.</p>
<div class="acct-foot"><button class="button">Bring your own OAuth client</button></div>
</div>
</div>
</section>
</div>
</div>
</body>
</html>