SCOPEOut

SCOPEOut Icon

SCOPEOut

An extension that generates custom cover letters for UBC Co-op jobs in a single click, including SCOPE, PD Portal, and more!

Chrome Extension JavaScript Gemini API License: GPL v3

Check it out! SCOPEOut on Chrome Web Store

πŸ“‘ Table of Contents

πŸ‘€ App Preview

SCOPEOut Popup SCOPEOut Prompt Editor Page
SCOPEOut Generated Cover Letter

✨ Features

πŸ› οΈ Installation

  1. Clone or download this repository:
    git clone https://github.com/ExxML/SCOPEOut.git
    
  2. Open your browser and navigate to the Manage Extensions page.
  3. Enable Developer mode.
  4. Click Load unpacked and select the src/ folder inside the cloned repository.

Note: This extension requires a Chromium-based browser that supports Manifest V3. Most modern browsers are compatible.

πŸš€ First-time Setup

  1. Click SCOPEOut in the Extensions menu to open the extension.
  2. Click Settings to expand the settings panel.
  3. Get a free Gemini API key and paste it in the field.
  4. Click Save. SCOPEOut will validate the key and fetch its available models. Once the key is confirmed to be valid, the Generate cover letter button will become enabled.
  5. Stay in the settings panel and select the Co-op Footer Image you wish to use (if any).
  6. Click Edit prompt and personalize your AI prompt by replacing the example text with your experiences/skills. Refer to Customising the Prompt for more detailed instructions.
  7. Once you have made your edits and saved your prompt, return to the extension and select your desired AI model. Refer to Choosing the Right Model to pick the model that works for you.
  8. The setup has been completed! You can now open any UBC Co-op job posting, click Generate cover letter, Download PDF, and ship your personalized cover letter to your dream company!

βš™οΈ Configuration

Customising the Prompt

  1. Open the popup and expand Settings.
  2. Click Edit prompt to open the Prompt Editor in a new tab.
  3. Modify the template as desired. Four placeholders are available:
    • {hiringManager} : the cover letter addressee scraped from the posting (defaults to β€œHiring Manager”)
    • {companyName} : the organisation name scraped from the posting
    • {jobTitle} : the cleaned job title scraped from the posting
    • {jobDescription} : the full job description scraped from the posting
  4. Click Save Prompt. The tab title shows (*) while there are unsaved changes.
  5. Click Restore Default to revert to the built-in template at any time.

[!NOTE] This prompt is saved in your browser’s local storage. Therefore, clearing your browser data or uninstalling SCOPEOut will delete your prompt, so we recommend keeping a backup in a separate document.

Choosing the Right Model

Use the Model dropdown in the popup to choose a Gemini model. The list is fetched from the Gemini API each time the popup opens, so new models appear automatically and deprecated ones are removed. Only general-purpose text-in, text-out models (including previews and -latest aliases) are shown, sorted newest first.

The newest Gemini models are often under heavy load and requests may frequently get rejected (and still use up your quota). If consistency matters to you, we recommend using slightly less popular models to avoid these hiccups.

Free tier rate limits vary by model; see Gemini API rate limits for up-to-date quotas.

πŸ’» Tech Stack

Layer Technology
Extension platform Chrome Extensions Manifest V3
Language Vanilla JavaScript (ES Modules)
AI backend Google Gemini API

πŸ“ Project Structure

SCOPEOut/
β”œβ”€β”€ src/
β”‚   β”œβ”€β”€ manifest.json                # Extension manifest (MV3)
β”‚   β”œβ”€β”€ assets/
β”‚   β”‚   β”œβ”€β”€ scope_out_icon_128.png   # 128x128 Extension icon
β”‚   β”‚   β”œβ”€β”€ scope_out_icon_2048.png  # Full resolution extension icon
β”‚   β”‚   β”œβ”€β”€ eng_coop_footer.png      # Engineering Co-op footer image for cover letter
β”‚   β”‚   └── science_coop_footer.png  # Science Co-op footer image for cover letter
β”‚   β”œβ”€β”€ api/
β”‚   β”‚   β”œβ”€β”€ gemini.js                # Gemini API client: lists models, builds prompt & calls generateContent
β”‚   β”‚   └── default-prompt.js        # Default cover letter prompt template with placeholders
β”‚   β”œβ”€β”€ background/
β”‚   β”‚   └── service-worker.js        # MV3 service worker: orchestrates scraping, generation & preview
β”‚   β”œβ”€β”€ content/
β”‚   β”‚   └── scraper.js               # Content script injected into job posting pages to extract job data
β”‚   β”œβ”€β”€ popup/
β”‚   β”‚   β”œβ”€β”€ popup.html               # Extension popup UI
β”‚   β”‚   β”œβ”€β”€ popup.css
β”‚   β”‚   └── popup.js                 # Popup controller: settings, model picker, generation trigger
β”‚   β”œβ”€β”€ preview/
β”‚   β”‚   β”œβ”€β”€ preview.html             # Cover letter preview page
β”‚   β”‚   β”œβ”€β”€ preview.css
β”‚   β”‚   └── preview.js               # Renders paginated, editable letter; handles PDF download
β”‚   └── prompt-editor/
β”‚       β”œβ”€β”€ prompt-editor.html       # Full-page prompt editor
β”‚       β”œβ”€β”€ prompt-editor.css
β”‚       └── prompt-editor.js         # Prompt editor controller: load, save, restore default
└── README.md

πŸ€– How It Works

User clicks "Generate cover letter"
        β”‚
        β–Ό
popup.js validates active tab URL
        β”‚
        β–Ό
Sends "startGeneration" message β†’ service-worker.js
        β”‚
        β–Ό
chrome.scripting.executeScript injects content/scraper.js
        β”‚   Extracts: hiringManager, companyName, jobTitle, jobDescription
        β–Ό
gemini.js builds prompt (replaces placeholders in stored/default template)
        β”‚
        β–Ό
POST to Gemini API (generateContent endpoint)
        β”‚
        β–Ό
Cover letter body returned β†’ stored in chrome.storage.session
        β”‚
        β–Ό
preview/preview.html opened in new tab
        β”‚   Renders paginated, editable letter
        β–Ό
User edits in-browser β†’ clicks "Download PDF" β†’ window.print()

Generation progress is written to chrome.storage.session at each stage (generating β†’ complete / error), so the popup displays live status updates even if it is closed and reopened during generation.

πŸ“ License

This project is licensed under the GNU General Public License v3.0.