From 36702ca98ccc62414ceb86111a45ab414ac11cca Mon Sep 17 00:00:00 2001 From: Tobi <155588579+spiky02plateau@users.noreply.github.com> Date: Wed, 16 Sep 2026 17:29:40 +0200 Subject: [PATCH] feat(inbox): open source conversations in Gmail --- MANUAL.md | 3 ++ docs/pr-images/gmail-link-after.png | Bin 0 -> 34221 bytes docs/pr-images/gmail-link-before.png | Bin 0 -> 35804 bytes src/feed/CardView.tsx | 10 ++-- src/feed/GmailLinks.tsx | 55 ++++++++++++++++++++ src/styles.css | 1 + test/gmail-links.test.tsx | 73 +++++++++++++++++++++++++++ 7 files changed, 139 insertions(+), 3 deletions(-) create mode 100644 docs/pr-images/gmail-link-after.png create mode 100644 docs/pr-images/gmail-link-before.png create mode 100644 src/feed/GmailLinks.tsx create mode 100644 test/gmail-links.test.tsx diff --git a/MANUAL.md b/MANUAL.md index 24cddaba..2e3fd2bf 100644 --- a/MANUAL.md +++ b/MANUAL.md @@ -136,6 +136,9 @@ The active card follows your reading position. On the feed screen: - `O` opens or closes the active email thread - action buttons show their keyboard shortcut when one is available +Email cards with a source mailbox and explicit Gmail thread references show **Open in Gmail** beneath the title. +The link opens the conversation in that account in a new tab, with separate links when a card covers several conversations. + ### Card Actions Card buttons describe the concrete next move, such as: diff --git a/docs/pr-images/gmail-link-after.png b/docs/pr-images/gmail-link-after.png new file mode 100644 index 0000000000000000000000000000000000000000..026feed891a55fd875537d952fd0ea600204dd0c GIT binary patch literal 34221 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 literal 0 HcmV?d00001 diff --git a/docs/pr-images/gmail-link-before.png b/docs/pr-images/gmail-link-before.png new file mode 100644 index 0000000000000000000000000000000000000000..4e3359abf30fc4d2ca0165a1304ed13a53228eed GIT binary patch literal 35804 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 void; readingFace?: boolean }) { +function Block({ feedId, cardId, block, sourceMailbox, onChanged, readingFace = false }: { feedId: string; cardId: string; block: CardBlock; sourceMailbox?: string; onChanged: () => void; readingFace?: boolean }) { const [value, setValue] = useState(block.value ?? ""); useEffect(() => setValue(block.value ?? ""), [block.value]); @@ -219,7 +220,7 @@ function Block({ feedId, cardId, block, onChanged, readingFace = false }: { feed {typeof item === "string" ? : item.href - ? {item.label} + ? {item.label} : } ))} @@ -460,6 +461,7 @@ function ReadingCardView({ card, active, reaction, group, preference, onVersion,

{card.title}

+

{card.why}

@@ -468,7 +470,7 @@ function ReadingCardView({ card, active, reaction, group, preference, onVersion,
{card.blocks.map((block) => block.type === "quote" ?

{block.text}

{block.attribution && {block.attribution}}
- : )} + : )}
View this source run @@ -545,6 +547,7 @@ export function CardView({
{card.eyebrow}

{card.title}

+

@@ -552,6 +555,7 @@ export function CardView({
{card.blocks.map((block) => (); + const gmailReceipt = card.blocks.some(block => block.type === "receipt" && /\bGmail\b/i.test(block.text ?? "")); + const addIds = (text: string) => { + for (const match of text.matchAll(/\b[0-9a-f]{12,20}\b/gi)) { + links.add(`https://mail.google.com/mail/u/?authuser=${encodeURIComponent(mailbox)}#all/${match[0].toLowerCase()}`); + } + }; + for (const block of card.blocks) { + if (block.type === "receipt" && gmailReceipt) { + for (const match of (block.text ?? "").matchAll(/\b(?:Gmail\s+)?thread\s+IDs?\s*:\s*([^.;\n]+)/gi)) addIds(match[1]); + } + if (block.type !== "evidence") continue; + for (const item of block.items ?? []) { + if (typeof item === "string") continue; + if (/^Gmail\s+thread(?:\s+IDs?)?$/i.test(item.label.trim()) + || (gmailReceipt && /^thread(?:\s+IDs?)?$/i.test(item.label.trim()))) addIds(item.detail ?? ""); + const href = item.href && gmailHref(item.href, mailbox); + if (!href) continue; + const hash = new URL(href).hash; + const thread = hash.match(/^#(?:inbox|all|sent|trash|spam)\/([0-9a-f]{12,20})$/i); + if (thread) addIds(thread[1]); + else if (hash.includes("/") || hash.startsWith("#thread-f:")) links.add(href); + } + } + return [...links]; +} + +export function GmailLinks({ card }: { card: Card }) { + const links = gmailLinks(card); + if (!links.length) return null; + return
{links.map((href, index) => ( + + {links.length === 1 ? "Open in Gmail" : `Open email ${index + 1} in Gmail`} + + ))}
; +} diff --git a/src/styles.css b/src/styles.css index e31aa8b4..0ef74e2a 100644 --- a/src/styles.css +++ b/src/styles.css @@ -57,6 +57,7 @@ kbd { font: 11px/1 var(--mono); color: var(--ink-3); } .attention-card.has-context-influence .card-rule { background: #b68552; } .attention-card.has-context-influence.is-active .card-rule { background: var(--blue); } .card-head { display: flex; align-items: flex-start; gap: 12px; } +.gmail-links { display: flex; flex-wrap: wrap; gap: 6px 14px; margin-top: 8px; font-size: 12px; } .kind-dot { flex: 0 0 auto; width: 10px; height: 10px; margin-top: 7px; border-radius: 50%; background: #82909b; } .kind-dot.proposal { background: #b98649; } .eyebrow { margin-bottom: 5px; color: var(--ink-3); font-size: 11px; font-weight: 700; letter-spacing: .12em; text-transform: uppercase; } diff --git a/test/gmail-links.test.tsx b/test/gmail-links.test.tsx new file mode 100644 index 00000000..30bca34f --- /dev/null +++ b/test/gmail-links.test.tsx @@ -0,0 +1,73 @@ +import { expect, test } from "bun:test"; +import { renderToStaticMarkup } from "react-dom/server"; +import type { Card } from "../shared/types"; +import { gmailHref, gmailLinks, GmailLinks } from "../src/feed/GmailLinks"; +import { CardView } from "../src/feed/CardView"; + +const card: Card = { + id: "source-email", feedId: "inbox", kind: "attention", status: "to_review_new", + title: "Workshop attendee list", eyebrow: "Email", why: "Review the attendee list.", + sourceMailbox: "owner+work@example.com", + blocks: [{ id: "receipt", type: "receipt", text: "Gmail thread IDs: 1234567890abcdef and fedcba0987654321." }], + readyForPass: 1, createdAt: "2026-09-16T12:00:00Z", updatedAt: "2026-09-16T12:00:00Z", history: [], +}; + +test("opens each referenced conversation in the source account, including archived mail", () => { + expect(gmailLinks(card)).toEqual([ + "https://mail.google.com/mail/u/?authuser=owner%2Bwork%40example.com#all/1234567890abcdef", + "https://mail.google.com/mail/u/?authuser=owner%2Bwork%40example.com#all/fedcba0987654321", + ]); +}); + +test("uses evidence references and deduplicates the same thread", () => { + expect(gmailLinks({ ...card, blocks: [{ + id: "sources", type: "evidence", items: [ + { label: "Gmail thread", detail: "1234567890abcdef" }, + { label: "Gmail thread", detail: "1234567890abcdef" }, + { label: "Original", href: "https://mail.google.com/mail/u/0/#inbox/fedcba0987654321" }, + ], + }] })).toEqual(gmailLinks(card)); +}); + +test("does not guess from the title, card ID, arbitrary text or an unrelated URL", () => { + expect(gmailLinks({ ...card, id: "email-1234567890abcdef", blocks: [ + { id: "memo", type: "memo", text: "Thread ID: 1234567890abcdef" }, + { id: "receipt", type: "receipt", text: "Transaction ID: 1234567890abcdef" }, + { id: "source", type: "evidence", items: [{ label: "Original", href: "https://mail.google.com.evil.example/#all/1234567890abcdef" }] }, + ] })).toEqual([]); + expect(gmailLinks({ ...card, sourceMailbox: undefined })).toEqual([]); +}); + +test("renders detached links and omits the control when no reference exists", () => { + const html = renderToStaticMarkup(); + expect(html).toContain("Open email 1 in Gmail"); + expect(html).toContain("Open email 2 in Gmail"); + expect(html).toContain('target="_blank" rel="noopener noreferrer"'); + expect(renderToStaticMarkup()).toBe(""); +}); + + +test("keeps explicit modern Gmail references and fixes their account selector", () => { + const href = "https://mail.google.com/mail/u/0/#inbox/FMfcgzExample"; + expect(gmailLinks({ ...card, blocks: [{ id: "source", type: "evidence", items: [{ label: "Original", href }] }] })) + .toEqual(["https://mail.google.com/mail/u/?authuser=owner%2Bwork%40example.com#inbox/FMfcgzExample"]); + expect(gmailHref(href, " owner@example.com ")).toContain("authuser=owner%40example.com"); + expect(gmailLinks({ ...card, sourceMailbox: " " })).toEqual([]); +}); + +test("requires Gmail context before interpreting plain thread IDs", () => { + expect(gmailLinks({ ...card, blocks: [{ id: "receipt", type: "receipt", text: "Thread ID: 1234567890abcdef." }] })).toEqual([]); +}); + +test("uses the source account in both the header and original evidence link", () => { + const html = renderToStaticMarkup( {}} onChanged={() => {}} + onAction={() => {}} onReturnToReview={() => {}} + />); + expect(html).toContain("Open in Gmail"); + expect(html).not.toContain("/mail/u/0/"); + expect(html.match(/authuser=owner%2Bwork%40example.com/g)).toHaveLength(2); +});