{
"manifest_version": 3,
"name": "Gmail AI Assistant",
"version": "0.3.0",
"description": "Intelligently sorts and prioritizes your emails.",
"permissions": [
"storage",
"activeTab",
"scripting"
],
"background": {
"service_worker": "background.js"
},
"content_scripts": [
{
"matches": ["https://mail.google.com/*"],
"js": ["content.js"],
"css": ["styles.css"]
}
],
"action": {
"default_popup": "popup.html",
"default_icon": {
"16": "images/icon16.png",
"48": "images/icon48.png",
"128": "images/icon128.png"
}
},
"icons": {
"16": "images/icon16.png",
"48": "images/icon48.png",
"128": "images/icon128.png"
}
}
```javascript
// background.js
// This script runs in the background.
// For now, it will just log a message to indicate it's running.
// In later phases, it will handle complex logic and API calls.
console.log("Gmail AI Assistant background script loaded.");
// Listener for when the extension is installed or updated
chrome.runtime.onInstalled.addListener(() => {
console.log("Gmail AI Assistant has been installed.");
// Initialize senderCounts in storage. This will store how many times an email is received from a sender.
chrome.storage.sync.set({ senderCounts: {} });
});
```javascript
// content.js
console.log("Gmail AI Assistant content script loaded and running.");
/**
* A list of keywords that often indicate a promotional or spam email.
*/
const PROMOTION_KEYWORDS = [
'unsubscribe', 'view in browser', 'no longer wish', 'special offer',
'limited time', 'percent off', 'free shipping', 'view this email'
];
/**
* Scans all visible emails for various patterns and injects contextual prompts.
*/
function processVisibleEmails() {
const emailRows = document.querySelectorAll('tr.zA');
if (emailRows.length === 0) return;
chrome.storage.sync.get(['senderCounts'], (result) => {
let counts = result.senderCounts || {};
let updated = false;
emailRows.forEach(row => {
if (row.dataset.promptInjected) return;
const senderEl = row.querySelector('.yW span[email]');
if (senderEl) {
const senderEmail = senderEl.getAttribute('email');
const senderName = senderEl.getAttribute('name');
// --- 1. Personal Conversation Logic ---
// A simple way to detect a conversation is to see if there's a thread count.
const threadCountEl = row.querySelector('.bA4 span');
if (threadCountEl && parseInt(threadCountEl.innerText) > 2) {
injectPersonalConversationPrompt(row, senderName);
row.dataset.promptInjected = 'true';
return;
}
// --- 2. Repetitive Sender Logic ---
if (!counts[senderEmail]) {
counts[senderEmail] = 0;
}
counts[senderEmail] += 1;
updated = true;
const threshold = 4; // Increased threshold to not conflict with conversation detection
if (counts[senderEmail] >= threshold) {
injectRepetitiveEmailPrompt(row, senderName, counts[senderEmail]);
row.dataset.promptInjected = 'true';
return;
}
// --- 3. Promotional Email Logic ---
const snippetEl = row.querySelector('.y2');
if (snippetEl) {
const snippetText = snippetEl.innerText.toLowerCase();
if (PROMOTION_KEYWORDS.some(keyword => snippetText.includes(keyword))) {
injectPromotionPrompt(row, senderName);
row.dataset.promptInjected = 'true';
return;
}
}
}
});
if (updated) {
chrome.storage.sync.set({ senderCounts: counts });
}
});
}
/**
* Injects a prompt for emails identified as an important personal conversation.
* @param {HTMLElement} targetRow The email row to add the prompt above.
* @param {string} senderName The name of the person in the conversation.
*/
function injectPersonalConversationPrompt(targetRow, senderName) {
const promptContainer = createPromptContainer();
const promptContent = document.createElement('div');
promptContent.className = 'gmail-ai-prompt personal'; // 'personal' class for unique styling
promptContent.innerHTML = `
<div class="prompt-icon">💬</div>
<div class="prompt-text">
This looks like an important conversation with <strong>${senderName}</strong>. Mark as priority?
</div>
<div class="prompt-actions">
<button class="prompt-button yes" data-action="mark-priority">Mark Priority</button>
<button class="prompt-button no" data-action="dismiss">Dismiss</button>
</div>
`;
promptContainer.firstChild.appendChild(promptContent);
targetRow.parentNode.insertBefore(promptContainer, targetRow);
// Add button functionality
promptContainer.querySelector('[data-action="dismiss"]').addEventListener('click', (e) => {
e.stopPropagation();
promptContainer.remove();
});
promptContainer.querySelector('[data-action="mark-priority"]').addEventListener('click', (e) => {
e.stopPropagation();
alert('Priority marking will be available in a future update!');
promptContainer.remove();
});
}
/**
* Injects a prompt for emails identified as likely promotions.
*/
function injectPromotionPrompt(targetRow, senderName) {
const promptContainer = createPromptContainer();
const promptContent = document.createElement('div');
promptContent.className = 'gmail-ai-prompt promotion';
promptContent.innerHTML = `
<div class="prompt-icon">🛍️</div>
<div class="prompt-text">
This looks like a promotional email from <strong>${senderName}</strong>.
</div>
<div class="prompt-actions">
<button class="prompt-button yes" data-action="unsubscribe">Unsubscribe</button>
<button class="prompt-button no" data-action="dismiss">Dismiss</button>
</div>
`;
promptContainer.firstChild.appendChild(promptContent);
targetRow.parentNode.insertBefore(promptContainer, targetRow);
promptContainer.querySelector('[data-action="dismiss"]').addEventListener('click', (e) => {
e.stopPropagation();
promptContainer.remove();
});
promptContainer.querySelector('[data-action="unsubscribe"]').addEventListener('click', (e) => {
e.stopPropagation();
alert('Unsubscribe functionality will be available in a future update!');
promptContainer.remove();
});
}
/**
* Injects the UI prompt for repetitive emails.
*/
function injectRepetitiveEmailPrompt(targetRow, senderName, count) {
const promptContainer = createPromptContainer();
const promptContent = document.createElement('div');
promptContent.className = 'gmail-ai-prompt repetitive';
promptContent.innerHTML = `
<div class="prompt-icon">💡</div>
<div class="prompt-text">
You've received <strong>${count}</strong> emails from <strong>${senderName}</strong>. Would you like to create a filter?
</div>
<div class="prompt-actions">
<button class="prompt-button yes" data-action="create-filter">Create Filter</button>
<button class="prompt-button no" data-action="dismiss">Dismiss</button>
</div>
`;
promptContainer.firstChild.appendChild(promptContent);
targetRow.parentNode.insertBefore(promptContainer, targetRow);
promptContainer.querySelector('[data-action="dismiss"]').addEventListener('click', (e) => {
e.stopPropagation();
promptContainer.remove();
});
promptContainer.querySelector('[data-action="create-filter"]').addEventListener('click', (e) => {
e.stopPropagation();
alert('Filter creation functionality is coming soon!');
promptContainer.remove();
});
}
/**
* Helper function to create the common structure for a prompt container row.
*/
function createPromptContainer() {
const container = document.createElement('tr');
container.className = 'gmail-ai-prompt-container';
const cell = document.createElement('td');
cell.colSpan = "100%";
container.appendChild(cell);
return container;
}
/**
* Observes the Gmail interface for changes and triggers email processing.
*/
function observeGmail() {
const observer = new MutationObserver((mutationsList) => {
for(const mutation of mutationsList) {
if (mutation.type === 'childList' && mutation.addedNodes.length > 0) {
setTimeout(processVisibleEmails, 500);
return;
}
}
});
observer.observe(document.body, { childList: true, subtree: true });
console.log("Mutation observer is now watching the Gmail interface.");
setTimeout(processVisibleEmails, 1000);
}
/**
* Injects a placeholder UI element to confirm our script is working.
*/
function injectInitialUI() {
const composeButton = document.querySelector('.T-I.T-I-KE.L3');
if (composeButton && !document.getElementById('gmail-ai-assistant-marker')) {
const ourUI = document.createElement('div');
ourUI.id = 'gmail-ai-assistant-marker';
ourUI.textContent = 'AI Assistant Active';
ourUI.style.padding = '5px 10px';
ourUI.style.backgroundColor = '#4285F4';
ourUI.style.color = 'white';
ourUI.style.marginLeft = '10px';
ourUI.style.borderRadius = '4px';
composeButton.parentElement.appendChild(ourUI);
}
}
// ---- Initialization ----
const initInterval = setInterval(() => {
if (document.querySelector('.Cp')) {
clearInterval(initInterval);
injectInitialUI();
observeGmail();
}
}, 500);
```css
/* styles.css */
/* This style ensures the container for our prompt doesn't have unwanted borders or lines from Gmail's default table styling. */
.gmail-ai-prompt-container {
border: none !important;
box-shadow: none !important;
}
/* The main container for our injected prompt. */
.gmail-ai-prompt {
border-radius: 8px;
padding: 12px 16px;
margin: 6px 16px 6px 72px; /* Aligns with Gmail's content, leaving space for checkboxes */
font-family: 'Google Sans', Roboto, Arial, sans-serif;
font-size: 14px;
display: flex;
align-items: center;
gap: 16px; /* Provides spacing between the icon, text, and buttons */
box-shadow: 0 1px 3px rgba(0,0,0,0.05);
}
/* --- Repetitive Email Prompt --- */
.gmail-ai-prompt.repetitive {
background-color: #f0f4f9; /* A light, friendly blue */
border: 1px solid #d4e3fb;
}
/* --- Promotional Email Prompt --- */
.gmail-ai-prompt.promotion {
background-color: #fce8e6; /* A light, cautionary red/pink */
border: 1px solid #f9d9d6;
}
.gmail-ai-prompt.promotion .prompt-text strong {
color: #c5221f; /* A darker red for highlighted text */
}
/* --- Personal Conversation Prompt --- */
.gmail-ai-prompt.personal {
background-color: #e6f4ea; /* A light, positive green */
border: 1px solid #cce8d4;
}
.gmail-ai-prompt.personal .prompt-text strong {
color: #1e8e3e; /* A darker green for highlighted text */
}
/* Styling for the lightbulb icon */
.prompt-icon {
font-size: 20px;
}
/* The text portion of the prompt */
.prompt-text {
flex-grow: 1; /* Allows the text to take up the remaining space */
color: #3c4043;
}
.prompt-text strong {
font-weight: 500;
color: #1a73e8; /* Default highlight color */
}
/* Container for the action buttons */
.prompt-actions {
display: flex;
gap: 8px;
}
/* Base styling for the prompt buttons */
.prompt-button {
background-color: transparent;
border: 1px solid #dadce0;
border-radius: 4px;
padding: 8px 16px;
font-family: inherit;
font-size: 14px;
font-weight: 500;
cursor: pointer;
transition: background-color 0.2s, box-shadow 0.2s;
}
/* Specific styling for the 'Yes' or confirmation button */
.prompt-button.yes {
background-color: #1a73e8;
color: white;
border-color: #1a73e8;
}
.prompt-button.yes:hover {
background-color: #287ae6;
box-shadow: 0 1px 2px 0 rgba(60,64,67,0.3), 0 1px 3px 1px rgba(60,64,67,0.15);
}
/* --- Promotion-specific button styling --- */
.gmail-ai-prompt.promotion .prompt-button.yes {
background-color: #d93025;
border-color: #d93025;
}
.gmail-ai-prompt.promotion .prompt-button.yes:hover {
background-color: #e04a40;
}
/* --- Personal-specific button styling --- */
.gmail-ai-prompt.personal .prompt-button.yes {
background-color: #1e8e3e;
border-color: #1e8e3e;
}
.gmail-ai-prompt.personal .prompt-button.yes:hover {
background-color: #25a24c;
}
/* Specific styling for the 'No' or dismiss button */
.prompt-button.no {
color: #5f6368;
}
.prompt-button.no:hover {
background-color: #f1f3f4;
}
```html
<!-- popup.html -->
<!DOCTYPE html>
<html>
<head>
<title>Gmail AI Assistant</title>
<style>
body {
font-family: 'Google Sans', Roboto, Arial, sans-serif;
width: 250px;
padding: 10px;
text-align: center;
}
h1 {
font-size: 16px;
color: #202124;
}
p {
font-size: 14px;
color: #5f6368;
}
</style>
</head>
<body>
<h1>Gmail AI Assistant</h1>
<p>The assistant is active and analyzing your inbox.</p>
<p>Future settings and controls will appear here.</p>
</body>
</html>
```
// Create dummy icon files named icon16.png, icon48.png, and icon128.png
// and place them in an 'images' folder. For now, they can be any simple image.
Comments