How to Build an AI Email Reply Generator Chrome Extension with JavaScript

Wrtitng email reply form an gpt required too much too and fro, example, if i want to generate a email reply, then first i have to copy the all content of email conversion then go to gpt and then paste it, and then asked gpt to generate the email reply then its give response, copy those response and again come to our gmail page and paste it, and some time happen that format of reply can be disttrub, so that we have to also correct those format. So, instead of doing these to and fro, as a developer, why don't we create an extension that has an embedded button in the reply box, and by clicking it, within a second, it will generate the reply based on all previous conversations?

In this article, we will go through creating your Chrome browser extension that integrates directly into the Gmail interface to generate an email reply. This "Email AI" will have two powerful features:

  1. AI Reply Generator: A button that reads the context of an email conversation and uses an AI (like Google's Gemini) to generate a relevant, professional reply.
  2. Template Inserter: A dropdown menu to instantly insert pre-written templates for common emails like birthday wishes or leave applications, etc.

This project is a fantastic way to learn about developing a Chrome extension, DOM manipulation, and integrating AI for our personal use in our application. Let's get started!

Prerequisites

Before we dive in, make sure you have the following:

  • A Code Editor IDE like VS Code.
  • Basic knowledge of JavaScript, HTML, and CSS.
  • An API key for an AI model (we will be using Google's Gemini, which has a generous free tier).

Project Structure: The Core Files

Our extension will be surprisingly simple, consisting of just two primary files:

1.  manifest.json: The configuration file. It tells Chrome what our extension is, what permissions it needs, and which scripts to run.

2.  content.js: Our main JavaScript file. This script will be injected into the Gmail page to add our buttons and handle all the logic. This basically contains the logic, or use can say it's a backend.

Let's create a new folder for our project and add these two empty files to it.

Step 1: Configuring the manifest.json File

The manifest.json file is the heart of any Chrome extension. It defines the extension's capabilities and permissions. Let's break down what each part does.

Copy the following code into your `manifest.json` file:

json

{
  "name": "Email Writer Assistant",
  "description": "AI-powered email reply generator and template tool.",
  "version": "1.0",
  "manifest_version": 3,
  "permissions": [
    "activeTab",
    "storage"
  ],
  "host_permissions": [
    "*://mail.google.com/*",
    "https://generativelanguage.googleapis.com/*"
  ],
  "content_scripts": [
    {
      "js": [
        "content.js"
      ],
      "matches": [
        "*://mail.google.com/*"
      ],
      "run_at": "document_end"
    }
  ],
  "action": {
    "default_title": "Email Writer Assistant"
  }
}

Breaking Down the Manifest:

  • name, description, version: Self-explanatory metadata for our Email reply generator extension.
  • manifest_version: 3: Specifies that we are using the latest and most secure version of the Chrome extension platform. In simple words its specifes the extension version.
  • permissions: We request "storage" to potentially save custom templates in the future and "activeTab" for general interaction.
  • host_permissions: This is crucial for security. :*//mail.google.com/*: Grants our extension permission to run on Gmail.
  • https://generativelanguage.googleapis.com/*: Grants permission to make API calls to Google's Gemini AI. 
  • content_scripts: This is where we tell Chrome to inject our content.js file into any page that matches the *://mail.google.com/* pattern. In simple words it tell the extension to run this file.

Step 2: Writing the Logic for our Extension - The content.js Script

This is where the magic happens. Our content script will watch for the Gmail compose window to open, then inject our custom buttons and define their functionality. So, using Xpath we will find the place where to inject buttons using javascript DOM, and we will fetch the content of the mail using javascript dom and then we will pass those content with prompt to the gemini api using javascript. And get the response from it and pase it into the reply box using javascript dom.

Copy the following code into your content.js file. We have adapted it to call the Gemini API directly.

JavaScript
  
console.log("Email Writer Extension - Content Script Loaded");
// --- Helper Functions to Create UI Elements ---
function createAIButton() {
    const button = document.createElement('div');
    button.className = 'T-I J-J5-Ji aoO v7 T-I-atl L3'; // Mimic Gmail's button style
    button.style.marginRight = '8px';
    button.innerHTML = 'AI Reply';
    button.setAttribute('role', 'button');
    button.setAttribute('data-tooltip', 'Generate AI Reply');
    return button;
}



function createTemplateDropdown() {
    const dropdown = document.createElement('select');
    dropdown.className = 'T-I J-J5-Ji aoO v7 T-I-atl L3 template-dropdown';
    dropdown.style.marginRight = '8px';
    dropdown.setAttribute('role', 'button');
    dropdown.setAttribute('data-tooltip', 'Insert Template');

    // Default option
    const defaultOption = document.createElement('option');
    defaultOption.text = 'Templates';
    defaultOption.disabled = true;
    defaultOption.selected = true;
    dropdown.appendChild(defaultOption);

    // Hardcoded templates
    const templates = [
        { name: "Birthday Wish", subject: "Happy Birthday ??", body: "Dear [Name],\n\nWishing you a very Happy Birthday! ????\n\nMay your day be filled with joy, love, and laughter.\n\nBest regards,\n[Your Name]" },
        { name: "Leave Application", subject: "Leave Application Request", body: "Dear [Manager's Name],\n\nI would like to request leave from [start date] to [end date] due to [reason].\n\nKindly approve my request.\n\nBest regards,\n[Your Name]" },
        { name: "Thank You Note", subject: "Thank You", body: "Dear [Recipient],\n\nI just wanted to say thank you for your support and guidance. I truly appreciate it.\n\nBest regards,\n[Your Name]" },
    ];

    templates.forEach(template => {
        const option = document.createElement('option');
        option.value = template.name;
        option.text = template.name;
        option.dataset.subject = template.subject;
        option.dataset.body = template.body;
        dropdown.appendChild(option);
    });

    dropdown.addEventListener('change', (e) => {
        const selected = e.target.selectedOptions[0];
        const subject = selected.dataset.subject;
        const body = selected.dataset.body;
        const subjectBox = document.querySelector('input[name="subjectbox"]');
        if (subjectBox) { subjectBox.value = subject; }
        const composeBox = document.querySelector('[role="textbox"][g_editable="true"]');
        if (composeBox) {
            composeBox.innerHTML = ""; // clear old content
            composeBox.focus();
            document.execCommand('insertText', false, body);
        }
        e.target.selectedIndex = 0; // Reset dropdown
    });
    return dropdown;

}

// --- Core Logic Functions ---
function getEmailContent() {
    // This function tries to find the content of the email being replied to.
    const quote = document.querySelector('.gmail_quote');
    if (quote) return quote.innerText.trim();

    // Fallback for different Gmail layouts
    const content = document.querySelector('.a3s.aiL');
    if (content) return content.innerText.trim();
    return '';
}

function findComposeToolbar() {

    // Gmail's class names can change. This looks for the toolbar using several potential selectors.
    return document.querySelector('.gU.Up') || document.querySelector('.aDh');
}

async function generateAIReply(emailContent) {
    const API_KEY = 'AIzaSyCXpVrzhqsDB5uXowkjftJsE2OYCpKHr3o'; // IMPORTANT: Replace with your actual key
    const API_URL = `https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent?key=${API_KEY}`;
    const prompt = `Generate a professional email reply for the following email content. Please don't generate a subject line. Email to reply to:\n\n---\n${emailContent}\n---`;
    try {
        const response = await fetch(API_URL, {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({
                contents: [{ parts: [{ text: prompt }] }]
            })
        });
        if (!response.ok) {
            throw new Error(`API request failed with status ${response.status}`);
        }
        const data = await response.json();
        // Navigate through the Gemini response structure to get the text
        return data.candidates[0].content.parts[0].text;

    } catch (error) {
        console.error("Error generating AI reply:", error);
        return "Sorry, I was unable to generate a reply. Please try again.";
    }

}


function injectButtons() {
    // Avoid duplicate buttons
    document.querySelector('.ai-reply-button')?.remove();
    document.querySelector('.template-dropdown')?.remove();
    const toolbar = findComposeToolbar();
    if (!toolbar) {
        console.log("Compose toolbar not found, will retry.");
        return;
    }

    // Always add the Templates dropdown
    const dropdown = createTemplateDropdown();
    toolbar.insertBefore(dropdown, toolbar.firstChild);

    // Only add the "AI Reply" button if we are replying to an email
    const emailContent = getEmailContent();
    if (emailContent && emailContent.length > 0) {
        const button = createAIButton();
        button.classList.add('ai-reply-button');
        button.addEventListener('click', async () => {
            button.innerHTML = 'Generating...';
            button.disabled = true;
            const generatedReply = await generateAIReply(emailContent);
            const composeBox = document.querySelector('[role="textbox"][g_editable="true"]');

            if (composeBox) {
                composeBox.focus();
                // We use execCommand for broad compatibility in content-editable divs
                document.execCommand('insertText', false, '\n\n' + generatedReply);
            }
            button.innerHTML = 'AI Reply';
            button.disabled = false;
        });
        toolbar.insertBefore(button, toolbar.firstChild);
    }
}

// --- Observer to Detect When the Compose Window Opens ---
const observer = new MutationObserver((mutations) => {
    for (const mutation of mutations) {

        // Check if a new node was added that looks like a compose window
        const composeWindowAdded = Array.from(mutation.addedNodes).some(node =>
            node.nodeType === Node.ELEMENT_NODE && node.querySelector('.gU.Up, .aDh')
        );

        if (composeWindowAdded) {
            console.log("Compose Window Detected, injecting buttons.");

            // Wait a moment for Gmail's UI to fully render before injecting
            setTimeout(injectButtons, 500);
            return; // No need to check other mutations
        }
    }
});

// Start observing the entire document for changes
observer.observe(document.body, {
    childList: true,
    subtree: true
});

IMPORTANT: Remember to replace 'YOUR_GEMINI_API_KEY' with your actual API key from the Google AI Studio. We will using the free version of the gemini api.

Step 3: Loading and Testing Your Extension

Now that the code is ready, let's load it into Chrome: Follows the following steps to use our own extension.
Open Chrome and navigate to chrome://extensions.


Turn on the "Developer mode" toggle in the top-right corner.


Click the "Load unpacked" button.


Select the folder where you saved your manifest.json and content.js files. Make sure to uplaod the whole folder.


If everything is correct, your "Email Writer Assistant" will appear in the list. 


Now, go to Gmail, refresh the page, and click "Compose." You should see the "Templates" dropdown in the compose toolbar. 


If you click "Reply" on an existing email, both the "AI Reply" button and the "Templates" dropdown will appear!

Potential Enhancements and Next Steps

This is a great starting point, but you can take it much further. Here are some ideas:
Tone Selection: Add another dropdown to let the user choose a tone (e.g., "Professional," "Casual," "Enthusiastic") and pass it to the AI prompt.
Custom Templates: Use chrome.storage.sync to allow users to save their own templates that sync across their devices.
Settings Page: Create a dedicated options page for managing the API key and custom templates.
Improved UI: Design custom icons for your buttons to make them look more polished.

Conclusion

Congratulations! You have successfully built a powerful AI Email Assistant for Gmail using JavaScript. You've learned how to create a Chrome extension, interact with a web page's DOM, and integrate a powerful AI model to automate a common task.

This project not only saves you time but also serves as a solid foundation for building even more complex and useful browser automation tools. Happy coding!
Previous Post
No Comment
Add Comment
comment url