import { demoCalendarInvitationEmails } from "@/lib/demo-calendar-invitation-emails"
export type EmailAttachmentKind = "pdf" | "image" | "other"
export interface EmailAttachment {
name: string
kind?: EmailAttachmentKind
/** Taille en octets (optionnelle) — tooltips / pills vs cartes dans l’aperçu */
sizeBytes?: number
/** Contenu texte inline (ex. ICS) — fixtures / import */
inlineText?: string
}
/** ICS ou métadonnées calendrier pour l’aperçu type Gmail */
export interface CalendarInvitationMeta {
/** Texte ICS (text/calendar) */
ics?: string
}
export interface ConversationMessage {
id: string
sender: string
senderEmail: string
/** ISO 8601 avec fuseau (ex. `2026-05-16T01:38:00+02:00`) */
date: string
body: string
preview: string
attachments?: EmailAttachment[]
/** Lu / non lu du message (fixtures legacy). */
read?: boolean
}
export interface Email {
id: string
sender: string
senderEmail?: string
/** @deprecated Utiliser `getThreadMessageCount(email)` — ancien compteur participants. */
participantCount?: number
subject: string
preview: string
/** HTML body — rendu dans une iframe sandbox pour raisons de sécurité */
body?: string
/** ISO 8601 avec fuseau — affichage via `formatMail*` / `MailDateText` */
date: string
read: boolean
starred: boolean
important: boolean
spam?: boolean
hasAttachment?: boolean
hasInvitation?: boolean
/** ICS / métadonnées — utilisé pour la carte invitation + détection visio */
calendarInvitation?: CalendarInvitationMeta
attachments?: EmailAttachment[]
tag?: string
/** Corbeille : pseudo-dossier `trash` quand true */
deleted?: boolean
/** Libellés / dossiers associés à cette conversation */
labels?: string[]
/** Messages précédents dans la conversation (le dernier message est le body principal) */
conversation?: ConversationMessage[]
/** ISO 8601 — heure d’envoi prévue (dossier Planifié) */
scheduledSendAt?: string
/** Prénom ou libellé pour la colonne « À : … » */
scheduledToName?: string
/** ISO 8601 — fin de mise en attente (dossier En attente) */
snoozeWakeAt?: string
/** Id du message le plus récent du fil (tête). Égal à `id` si message seul. */
threadHeadId?: string
/** Ids de tous les messages du fil, du plus ancien au plus récent. */
threadMessageIds?: string[]
/** En mode conversation, seuls les messages tête apparaissent en liste. */
isThreadHead?: boolean
}
export {
getThreadMessageCount,
normalizeLegacyEmailCatalog,
} from "@/lib/mail-thread"
import { normalizeLegacyEmailCatalog } from "@/lib/mail-thread"
const legacyEmails: Email[] = [
...demoCalendarInvitationEmails,
{
id: "1",
sender: "ronenrozn... Daniel",
senderEmail: "notifications@github.com",
participantCount: 3,
subject: "Re: [ollama/ollama] 0.23.1 : mlx runner failed (Issue #16007)",
preview: "- Closed #16007 as completed via #16053. — Reply to this email directly, view it on GitHub, or unsubscribe. You are receiving this bec...",
body: `
`,
date: "2026-05-16T01:38:00+02:00",
read: false,
starred: false,
important: false,
hasAttachment: true,
attachments: [
{ name: "CELTICOM_bulletin.pdf", kind: "pdf", sizeBytes: 482_910 },
{ name: "mlx_trace.png", kind: "image", sizeBytes: 128_440 },
{ name: "notes.txt", kind: "other", sizeBytes: 2_048 },
{ name: "build_log.pdf", kind: "pdf", sizeBytes: 1_024_512 },
],
labels: ["inbox"],
conversation: [
{
id: "1-a",
sender: "ronenrozn",
senderEmail: "ronenrozn@users.noreply.github.com",
date: "2026-05-12T23:15:00+02:00",
read: false,
preview: "After upgrading to 0.23.1, the mlx runner fails to start on Apple Silicon. Error: mlx_runner: failed to load model...",
body: `
After upgrading to 0.23.1, the mlx runner fails to start on Apple Silicon.
Error: mlx_runner: failed to load model
at MLXRunner.init (runner.go:142)
at Server.loadModel (server.go:89)
Environment:
- macOS 15.2 (Apple M3 Pro)
- ollama 0.23.1
`,
},
{
id: "1-b",
sender: "Daniel",
senderEmail: "daniel@ollama.ai",
date: "2026-05-13T00:42:00+02:00",
preview: "Thanks for reporting. This is a known regression in the MLX backend. We have a fix in #16053 that should resolve...",
body: `
Thanks for reporting. This is a known regression in the MLX backend.
We have a fix in #16053 that should resolve this. Can you try building from the fix/mlx-runner branch?
`,
},
],
},
{
id: "2",
sender: "Reçu Uber",
senderEmail: "noreply@uber.com",
participantCount: 2,
subject: "[Personal] Votre course de jeudi matin en Uber",
preview: "- 7 mai 2026 1:11 7 mai 2026 , 1:11 Eliott, merci d'avoir utilisé Uber Nous espérons que vous avez apprécié votre course ce ma...",
body: `
Eliott, merci d'avoir utilisé Uber
Nous espérons que vous avez apprécié votre course ce matin.
| Date | 7 mai 2026, 1:11 |
| Trajet | Paris 11e → Gare de Lyon |
| Total | 12,34 € |
`,
date: "2026-05-07T01:11:00+02:00",
read: true,
starred: false,
important: false,
attachments: [{ name: "recu_course.pdf", kind: "pdf", sizeBytes: 88_320 }],
labels: ["inbox", "Factures", "Achats", "Promotions"],
},
{
id: "3",
sender: "SannyGro., thehelme.",
senderEmail: "notifications@github.com",
participantCount: 2,
subject: "Re: [IceWhaleTech/ZimaOS] [Info] Installation on Proxmox VE 8.0.4 as a VM (Issue #5)",
preview: "- thehelmethedheinsight left a comment (IceWhaleTech/ZimaOS#5) I just wrote the .img t...",
body: `
thehelmethedheinsight left a comment (IceWhaleTech/ZimaOS#5)
I just wrote the .img to a usb stick with Balena Etcher, but linux 'dd' will work as well.
In Proxmox > Create VM, don't add media to the CD/DVD-rom device. ADD a USB device, plug in the USB stick that you imaged to (Etcher, dd).
Redirect Proxmox USB dev to that USB stick. Choose a UEFI BIOS, start the VM. During startup, press ESC to go into BIOS > set boot order - USB first. Proxmox should boot off the physical usb stick.
Install to your virtual HDD with the ZimaOS setup. I chose 64GB to play around with.
Reply to this email directly, view it on GitHub, or unsubscribe.
You are receiving this because you commented.
`,
date: "2026-04-05T12:00:00+02:00",
read: true,
starred: false,
important: false,
labels: ["inbox", "Réseaux sociaux"],
conversation: [
{
id: "3-a",
sender: "SannyGrooves",
senderEmail: "sannygrooves@users.noreply.github.com",
date: "2026-03-14T11:55:00+01:00",
preview: "SannyGrooves left a comment (IceWhaleTech/ZimaOS#5) Just wanted to say: Big shoutout to Rogger for this automated installation script. Just run in Proxmox SSH (",
body: `
SannyGrooves left a comment (IceWhaleTech/ZimaOS#5)
Just wanted to say: Big shoutout to Rogger for this automated installation script. Just run in Proxmox SSH and it does everything for you.
Reply to this email directly, view it on GitHub, or unsubscribe.
You are receiving this because you commented.
`,
},
],
},
{
id: "4",
sender: "parse-git., Istarkgv",
senderEmail: "notifications@github.com",
participantCount: 2,
subject: "Re: [parse-community/parse-server] Implement an MCP Server to let AI help with debugging Parse Server Instances, SCHEMAs, data... (Issue #9948)",
preview: "- Istarkgv left a comment ...",
body: ``,
date: "2026-03-19T10:00:00+01:00",
read: true,
starred: false,
important: false,
labels: ["inbox", "Mises à jour"],
},
{
id: "5",
sender: "Pyxage .. Argenis",
senderEmail: "notifications@github.com",
participantCount: 7,
subject: "Re: [zeroclaw-labs/zeroclaw] Feature parity checklist: OpenClaw → ZeroClaw migration blockers (Issue #88)",
preview: "- Closed #88 as completed via 80ce59f. You are receiving this bec...",
body: ``,
date: "2026-02-28T12:00:00+01:00",
read: true,
starred: true,
important: false,
labels: ["inbox", "starred", "Travail", "Forums"],
conversation: [
{
id: "5-a",
sender: "Pyxage",
senderEmail: "pyxage@users.noreply.github.com",
date: "2026-02-10T14:22:00+01:00",
preview: "This issue tracks the remaining feature parity gaps between OpenClaw and ZeroClaw. Blockers: 1. Plugin system...",
body: `
This issue tracks the remaining feature parity gaps between OpenClaw and ZeroClaw.
Blockers:
- Plugin system — needs adapter layer
- Config migration tool — partial
- Webhook retry logic — missing
- Rate limiting — different algorithm
`,
},
{
id: "5-b",
sender: "Argenis",
senderEmail: "argenis@zeroclaw-labs.com",
date: "2026-02-19T09:10:00+01:00",
preview: "Update: items 1-3 are resolved. Rate limiting will ship in v2.4. Moving to close once CI passes...",
body: `
Update: items 1-3 are resolved. Rate limiting will ship in v2.4.
Moving to close once CI passes on 80ce59f.
`,
},
],
},
{
id: "6",
sender: "Reçu Uber",
senderEmail: "noreply@uber.com",
participantCount: 2,
subject: "[Personal] Votre course de samedi matin en Uber",
preview: "- 28 févr. 2026 2:10 28 févr. 2026 , 2:10 Voici le récapitulatif des frais Ce document confirme que votre trajet est terminé. T...",
body: `
Voici le récapitulatif des frais
Ce document confirme que votre trajet est terminé.
| Date | 28 févr. 2026, 2:10 |
| Trajet | Montmartre → Bastille |
| Total | 18,50 € |
`,
date: "2026-02-28T02:10:00+01:00",
read: false,
starred: false,
important: true,
hasInvitation: true,
labels: ["inbox", "Factures"],
},
{
id: "7",
sender: "vercell[., iaraong.",
senderEmail: "notifications@github.com",
participantCount: 3,
subject: "Re: [wiarabeauty/frontend] Tabbar (PR #138)",
preview: "- Merged #138 into dev. — Reply to this email directly, view it on GitHub, or unsubscribe. You are receiving this because you w...",
body: ``,
date: "2026-02-27T12:00:00+01:00",
read: true,
starred: false,
important: false,
labels: ["sent", "[Imap]/Sent"],
},
{
id: "8",
sender: "iaraongit, vercell[b.",
senderEmail: "notifications@github.com",
participantCount: 3,
subject: "[wiarabeauty/frontend] Dev (PR #135)",
preview: "- You can view, comment on, or merge this pull request online at: https://github.com/wiarabeauty/frontend/pull/135 Commit Summary ...",
body: ``,
date: "2026-02-23T12:00:00+01:00",
read: true,
starred: false,
important: false,
labels: ["drafts"],
},
{
id: "9",
sender: "vercell[., iaraong.",
senderEmail: "notifications@github.com",
participantCount: 3,
subject: "Re: [wiarabeauty/frontend] Dev (PR #130)",
preview: "- Closed #130. You are receiving this because you were mentioned.Message ID:
Closed #130.
—
You are receiving this because you were mentioned.
Message ID: <wiarabeauty/frontend/pull/130/issue_event/22471S3>
`,
date: "2026-02-23T15:30:00+01:00",
read: true,
starred: false,
important: false,
labels: ["inbox", "CCTV"],
},
{
id: "10",
sender: "Reçu Uber",
senderEmail: "noreply@uber.com",
participantCount: 2,
subject: "[Personal] Votre course de vendredi matin en Uber",
preview: "- 30 janv. 20263:5530 janv. 2026 , 3:55Voici le récapitulatif des frais Ce document confirme que votre trajet est terminé...",
body: `
Voici le récapitulatif des frais
Ce document confirme que votre trajet est terminé.
| Date | 30 janv. 2026, 3:55 |
| Total | 22,10 € |
`,
date: "2026-02-02T03:55:00+01:00",
read: true,
starred: false,
important: false,
labels: ["inbox", "Déplacements"],
},
{
id: "11",
sender: "Iman .. Pegasus",
senderEmail: "notifications@github.com",
participantCount: 4,
subject: "Re: [parse-community/parse-dashboard] Time zone support (#482)",
preview: "- leonace924 left a comment (parse-community/parse-dashboard#482) @mtrezza What is your worki...",
body: ``,
date: "2026-01-30T12:00:00+01:00",
read: true,
starred: false,
important: false,
labels: ["inbox", "Clients"],
},
{
id: "12",
sender: "Chez Ma Tante",
senderEmail: "contact@chezmatante.fr",
participantCount: 2,
subject: "Invitation à rejoindre Fast Forward sur Chez Ma Tante",
preview: "- Bienvenue ! 🎉 Bonjour Jean Dupont, Vous avez été invité à rejoindre Fast Forward sur Chez Ma Tante ! Voici vos identifi...",
body: `
Bienvenue ! 🎉
Bonjour Jean Dupont,
Vous avez été invité à rejoindre Fast Forward sur Chez Ma Tante !
Voici vos identifiants de connexion :
- Email : jean.dupont@example.com
- Mot de passe temporaire : ********
Se connecter
`,
date: "2026-01-09T12:00:00+01:00",
read: true,
starred: false,
important: false,
hasInvitation: true,
labels: ["inbox", "BrowserAlerts", "Twitch"],
},
{
id: "13",
sender: "parse-git. .. Taufik",
senderEmail: "notifications@github.com",
participantCount: 4,
subject: "[parse-community/parse-server] Google Auth: No sessionToken sent from linkWith or loginWith (Issue #8853)",
preview: "- Thanks for opening this issue! 🎯 You can help us...",
body: `
Thanks for opening this issue! 🎯
You can help us by providing the following information:
- Parse Server version
- Node.js version
- Steps to reproduce
`,
date: "2025-12-29T12:00:00+01:00",
read: true,
starred: false,
important: false,
labels: ["inbox", "Q1 2026", "CMSecurity Alerts"],
},
{
id: "14",
sender: "parse-git. .. Manuel",
senderEmail: "notifications@github.com",
participantCount: 12,
subject: "Re: [parse-community/parse-server] docs: Added AI Integration section to use parse-mcp to help AI Agents interact with Parse (PR #9949)",
preview: "- mtrezza left a comment (parse-c...",
body: `
mtrezza left a comment on parse-community/parse-server#9949
Great addition to the docs! A few suggestions:
- Add a note about authentication requirements
- Include example MCP configuration
- Mention supported Parse Server versions
`,
date: "2025-12-01T12:00:00+01:00",
read: true,
starred: false,
important: false,
labels: ["inbox", "Travail", "RH"],
conversation: [
{
id: "14-a",
sender: "parse-github-assistant",
senderEmail: "parse-github-assistant[bot]@users.noreply.github.com",
date: "2025-11-28T10:00:00+01:00",
preview: "Thanks for opening this pull request! A maintainer will review it shortly...",
body: `
Thanks for opening this pull request! 🎯
A maintainer will review it shortly. Please make sure:
- Tests pass
- Documentation is updated
- Changelog entry added
`,
},
{
id: "14-b",
sender: "R3D347HR4Y",
senderEmail: "r3d347hr4y@users.noreply.github.com",
date: "2025-11-28T14:32:00+01:00",
preview: "Here is the initial PR adding the AI Integration docs section. It covers MCP server setup, authentication...",
body: `
Here is the initial PR adding the AI Integration docs section. It covers:
- MCP server setup and configuration
- Authentication flow for AI agents
- Example queries and responses
Happy to iterate based on feedback!
`,
},
{
id: "14-c",
sender: "dblythy",
senderEmail: "dblythy@users.noreply.github.com",
date: "2025-11-29T08:15:00+01:00",
preview: "Looks good overall! One concern: the auth section should mention that Parse Server keys are required...",
body: `
Looks good overall! One concern: the auth section should mention that Parse Server master key is never to be exposed to AI agents in production.
Suggestion: add a security callout box.
`,
},
],
},
{
id: "15",
sender: "parse-githu., Manuel",
senderEmail: "notifications@github.com",
participantCount: 2,
subject: "Re: [parse-community/parse-server] Allow editing createdAt in cloud code (Issue #9889)",
preview: "- parse-github-assistant[bot] left a comment (parse-community/parse-serve...",
body: `
parse-github-assistant[bot] left a comment on parse-community/parse-server#9889
This issue has been automatically marked as stale because it has not had recent activity.
`,
date: "2025-11-05T12:00:00+01:00",
read: true,
starred: false,
important: false,
labels: ["inbox"],
},
{
id: "16",
sender: "Reçu Uber",
senderEmail: "noreply@uber.com",
participantCount: 2,
subject: "[Personal] Votre course de samedi matin en Uber",
preview: "- 1 nov. 2025 2:55 1 nov. 2025 , 2:55 Eliott, merci d'avoir utilisé Uber Nous espérons que vous avez apprécié votre course ...",
body: `
Eliott, merci d'avoir utilisé Uber
Nous espérons que vous avez apprécié votre course ce samedi matin.
`,
date: "2025-11-01T02:55:00+01:00",
read: true,
starred: false,
important: false,
labels: ["inbox", "Voyages"],
},
{
id: "17",
sender: "GitHub",
senderEmail: "noreply@github.com",
subject: "[GitHub] Your personal access token (classic) is about to expire",
preview: "- Hi @R3D347HR4Y, We noticed your personal access token (classic) \"caprover-test\" with read.enterprise and...",
body: `
Hi @R3D347HR4Y,
We noticed your personal access token (classic) "caprover-test" with read.enterprise scope is about to expire.
To avoid any disruptions, please regenerate your token before it expires.
Regenerate token
`,
date: "2025-09-30T12:00:00+02:00",
read: true,
starred: false,
important: false,
hasInvitation: true,
attachments: [
{ name: "screenshot.png", kind: "image", sizeBytes: 412_000 },
{ name: "notes.txt", kind: "other", sizeBytes: 1_280 },
],
labels: ["inbox", "Finance"],
},
{
id: "18",
sender: "olivier delmas",
senderEmail: "olivier.delmas.1@gmail.com",
participantCount: 6,
subject:
"Invitation: Task Force : rdv en incidents - jeu. 14 mai 2026 14:30 - 15:30 (UTC+2) (moi@example.com)",
preview:
"Invitation à un événement Google Meet : Task Force : rdv en incidents — jeu. 14 mai 2026 14:30 – 15:30 (heure d’Europe centrale)",
body: `
Vous êtes invité·e à une réunion Google Meet.
Task Force : rdv en incidents
jeu. 14 mai 2026 14:30 – 15:30 (heure d’Europe centrale)
Rejoindre la réunion
`,
date: "2025-09-30T10:13:00+02:00",
read: true,
starred: false,
important: false,
hasInvitation: true,
labels: ["inbox", "Travail"],
hasAttachment: true,
calendarInvitation: {
ics: `BEGIN:VCALENDAR
VERSION:2.0
CALSCALE:GREGORIAN
METHOD:REQUEST
BEGIN:VEVENT
DTSTART;TZID=Europe/Paris:20260514T143000
DTEND;TZID=Europe/Paris:20260514T153000
DTSTAMP:20260513T101300Z
ORGANIZER;CN=Olivier Delmas:MAILTO:olivier.delmas.1@gmail.com
UID:demo-task-force@ultimail.test
ATTENDEE;CN=maz@iarabeauty.com:MAILTO:maz@iarabeauty.com
ATTENDEE;CN=agathe@iarabeauty.com:MAILTO:agathe@iarabeauty.com
ATTENDEE;CN=melanie.grc@hotmail.fr:MAILTO:melanie.grc@hotmail.fr
ATTENDEE;CN=foujeupavel@example.com:MAILTO:foujeupavel@example.com
SUMMARY:Task Force : rdv en incidents
LOCATION:https://meet.google.com/abc-defg-hij
DESCRIPTION:Réunion visio\\nLien Meet: https://meet.google.com/abc-defg-hij
END:VEVENT
END:VCALENDAR`,
},
attachments: [
{
name: "invite.ics",
kind: "other",
sizeBytes: 3_400,
inlineText: `BEGIN:VCALENDAR
VERSION:2.0
METHOD:REQUEST
BEGIN:VEVENT
DTSTART;TZID=Europe/Paris:20260514T143000
DTEND;TZID=Europe/Paris:20260514T153000
ORGANIZER;CN=Olivier Delmas:MAILTO:olivier.delmas.1@gmail.com
UID:demo-task-force@ultimail.test
ATTENDEE;CN=maz@iarabeauty.com:MAILTO:maz@iarabeauty.com
ATTENDEE;CN=agathe@iarabeauty.com:MAILTO:agathe@iarabeauty.com
ATTENDEE;CN=melanie.grc@hotmail.fr:MAILTO:melanie.grc@hotmail.fr
ATTENDEE;CN=foujeupavel@example.com:MAILTO:foujeupavel@example.com
SUMMARY:Task Force : rdv en incidents
LOCATION:https://meet.google.com/abc-defg-hij
END:VEVENT
END:VCALENDAR`,
},
{ name: "notes.txt", kind: "other", sizeBytes: 890 },
],
},
{
id: "19",
sender: "Service livraison express",
senderEmail: "shipping-express-notif@scam-domain.xyz",
subject: "Dernier avis : colis bloqué — frais douaniers à payer sous 24 h",
preview: "- Votre envoi n°4829103 est retenu. Régularisez ici : bit.ly/... (ne pas répondre à ce message)",
body: `
⚠️ ATTENTION : Dernier avis avant retour à l'expéditeur
Votre envoi n°4829103 est retenu en douane. Des frais de 2,99 € doivent être réglés sous 24 h.
PAYER MAINTENANT
Ne pas répondre à ce message.
`,
date: "2025-09-26T12:00:00+02:00",
read: false,
starred: false,
important: false,
spam: true,
labels: ["spam"],
},
{
id: "20",
sender: "Reçu Uber",
senderEmail: "noreply@uber.com",
participantCount: 2,
subject: "[Personal] Votre course de dimanche matin en Uber",
preview: "- Total 66,61 € 21 septembre 2025 Merci d'avoir choisi Uber One, Eliott Nous espérons que vous avez apprécié votre c...",
body: `
Merci d'avoir choisi Uber One, Eliott
Nous espérons que vous avez apprécié votre course ce dimanche matin.
| Date | 21 septembre 2025 |
| Total | 66,61 € |
`,
date: "2025-09-21T12:00:00+02:00",
read: true,
starred: false,
important: false,
labels: ["inbox", "Famille"],
},
{
id: "21",
sender: "LottoVIP",
senderEmail: "winner-notification@lottovip-scam.net",
subject: "Félicitations ! Vous êtes le grand gagnant — réclamez votre prix",
preview: "- Cliquez maintenant pour recevoir 1 000 000 €. Offre limitée. Désabonnement impossible.",
body: `
🎉 FÉLICITATIONS ! 🎉
Vous avez été sélectionné comme GRAND GAGNANT de notre loterie internationale !
1 000 000 €
Pour réclamer votre prix, cliquez sur le lien ci-dessous :
RÉCLAMER MON PRIX
Offre limitée. Désabonnement impossible.
`,
date: "2025-09-20T12:00:00+02:00",
read: true,
starred: false,
important: true,
spam: true,
labels: ["spam"],
},
{
id: "22",
sender: "Les Amis du Louvre",
senderEmail: "newsletter@louvre.fr",
subject: "Lettre des Amis du Louvre — mai 2026",
preview: "- Expositions, conférences et coulisses du musée : découvrez le programme du mois.",
body: `
Bonjour,
Voici la lettre de mai des Amis du Louvre : parcours Napoléon, nocturnes et ateliers jeune public.
Lire la lettre en ligne
`,
date: "2026-05-12T08:00:00+02:00",
read: false,
starred: false,
important: false,
labels: ["inbox", "Newsletters"],
},
{
id: "23",
sender: "The Washington Post",
senderEmail: "newsletters@washpost.com",
subject: "The Daily 202 : ce qu’il faut retenir ce matin",
preview: "- Analyses politiques et agenda de Washington — édition du 11 mai.",
body: ``,
date: "2026-05-11T07:00:00+02:00",
read: true,
starred: false,
important: false,
labels: ["inbox", "Newsletters"],
},
]
export const emails: Email[] = normalizeLegacyEmailCatalog(legacyEmails)