import { useState, useEffect, useRef, useCallback } from "react";
// ════════════════════════════════════════════════════════════════════════════
// CONFIG ← Fill these two in before deploying
// ════════════════════════════════════════════════════════════════════════════
const GOOGLE_CLIENT_ID = "514858431339-5v6bb7egs8cirjnp1h4fa24tso511acp.apps.googleusercontent.com"; // e.g. "123456789-abc.apps.googleusercontent.com"
const GEMINI_WORKER_URL = "https://twilight-dew-e570.jacobdsiler.workers.dev";
// ─── Gmail REST base ─────────────────────────────────────────────────────────
const GMAIL = "https://gmail.googleapis.com/gmail/v1/users/me";
let _tok = null; // access token – refreshed via GSI popup
async function gFetch(path, opts = {}) {
const r = await fetch(`${GMAIL}${path}`, {
...opts,
headers: {
Authorization: `Bearer ${_tok}`,
"Content-Type": "application/json",
...(opts.headers || {}),
},
});
if (r.status === 401) { _tok = null; throw new Error("auth_expired"); }
if (!r.ok) throw new Error(`Gmail ${r.status}: ${await r.text().catch(() => "")}`);
return r.json();
}
// ─── Body / header helpers ────────────────────────────────────────────────────
function b64(s) {
try { return atob(s.replace(/-/g, "+").replace(/_/g, "/")); } catch { return ""; }
}
function header(headers = [], name) {
return headers.find(h => h.name.toLowerCase() === name.toLowerCase())?.value || "";
}
function stripHtml(html) {
return html.replace(/
M
MailCtrl
Sign in with your Google account to manage Gmail — archive, label, unsubscribe, and AI-sort your inbox without the noise.
{!GOOGLE_CLIENT_ID ? (
⚠ Set GOOGLE_CLIENT_ID at the top of MailCtrl.jsx before deploying.
Get one at console.cloud.google.com → APIs & Services → Credentials → OAuth 2.0 Client ID.
Enable the Gmail API and add your domain as an Authorized JavaScript Origin.
) : (
)}
Scopes: gmail.modify
Read, archive, label, and trash threads.
No data leaves your browser except to Gmail's API.