Complete guide to developing and debugging the Late Meet Chrome extension
This guide is designed to help contributors set up their development environment, debug different components of the extension, and resolve common issues efficiently.
- Dev Environment Setup
- Debugging the Service Worker
- Debugging Content Scripts
- Debugging the Offscreen Document
- Debugging the Side Panel
- Common Errors and Fixes
- Hot Reload Workflow
Before starting development, ensure you have:
- Node.js 18+ (download)
- Google Chrome (latest stable version)
- VS Code (recommended)
git clone https://github.com/shouri123/Late-Meet.git
cd Late-Meet
npm install- Open Chrome and navigate to
chrome://extensions/ - Toggle Developer mode in the top-right corner
- You should now see Load unpacked button
npm run devThis starts Vite in watch mode, rebuilding the extension automatically when you save files.
Now:
- Open
chrome://extensions/ - Click Load unpacked
- Navigate to the
Late-Meetfolder and select thedist/folder- If
dist/doesn't exist, manually build:npm run build
- If
Install these extensions in VS Code for an optimal development experience:
| Extension | Purpose |
|---|---|
| ES7+ React/Redux/React-Native snippets | TypeScript snippets |
| Chrome Debugger | Debug extension directly from VS Code |
| Prettier | Auto-format code |
| ESLint | Lint TypeScript/JavaScript |
| Chrome DevTools | Official VS Code DevTools integration |
Enable these flags for better debugging:
- Open
chrome://flags/#extension-mime-request-handling- Set to Default
- Open
chrome://flags/#enable-extension-activity-log- Set to Enabled
- Restart Chrome
The service worker (src/background.ts) runs in the background and handles:
- Meeting detection and state management
- Communication between content scripts and UI components
- API requests to OpenAI and ElevenLabs
This is the primary way to debug service workers in Manifest V3:
- Open a new tab and navigate to
chrome://serviceworker-internals/ - Find "Late Meet" in the list of extensions
- Click inspect button next to "Late Meet"
- A DevTools window opens showing the service worker context
In the DevTools Console:
// Check current meeting state
chrome.storage.local.get(["meetingState"], (data) => {
console.log("Current state:", data);
});
// Send a test message to content script
chrome.tabs.query({ url: "*://meet.google.com/*" }, (tabs) => {
if (tabs[0]) {
chrome.tabs.sendMessage(tabs[0].id, { type: "TEST_MESSAGE" });
}
});
// View all registered listeners
// (shown automatically in Service Worker DevTools)Check if service worker is registered:
// In background.ts console
console.log("Service worker active");Monitor chrome.storage changes:
chrome.storage.onChanged.addListener((changes, namespace) => {
console.log(`[${namespace}] Storage changed:`, changes);
});Monitor message traffic:
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
console.log("Message received:", { message, sender: sender.url });
sendResponse({ success: true });
});- If your service worker stops responding, reload the extension:
- Go to
chrome://extensions/ - Click the reload icon under "Late Meet"
- Go to
The content script (src/content.ts) runs on Google Meet pages and handles:
- Detecting participant changes
- Capturing audio and meeting state
- Injecting UI elements into the Meet page
- Communicating with the service worker
- Open Google Meet in a tab
- Right-click on the page → Inspect (or press
F12) - Go to Console tab
- You're now in the page's context (not the extension context)
Content scripts have access to both the page and extension APIs:
// Check if content script is injected
console.log("Content script loaded");
// Send message to service worker
chrome.runtime.sendMessage({ type: "GET_MEETING_STATE" }, (response) =>
console.log("Response:", response),
);
// Monitor participant changes
const observer = new MutationObserver(() => {
console.log("DOM changed - participants may have changed");
});
observer.observe(document.body, { childList: true, subtree: true });In Chrome DevTools:
- Go to Sources tab
- Left sidebar → top-level domain (e.g.,
meet.google.com) - Look for content scripts folder
- Find and click
content.ts(shows compiled JavaScript) - Set breakpoints directly in the code
// In Meet tab console:
// Check if content script can reach service worker
chrome.runtime.sendMessage({ type: "PING" }, (response) => {
if (chrome.runtime.lastError) {
console.error("Service worker not responding:", chrome.runtime.lastError);
} else {
console.log("Service worker responded:", response);
}
});| Issue | Debug Steps |
|---|---|
| Content script not injected | Check manifest.json matches pattern; verify extension is loaded |
| Message not reaching service worker | Open service worker DevTools; check message listener exists |
| DOM not accessible | Content script must run after document_idle; check manifest setting |
The offscreen document (src/offscreen.ts) handles:
- Background audio capture processing
- WebAudio API operations
- Heavy computations that need DOM access
- Open
chrome://extensions/ - Click Details for Late Meet
- Scroll to Extension Manifest section
- Find the offscreen page in the "Offscreen pages" section
- Click inspect next to the listed offscreen page
-
When your extension creates an offscreen document:
// In background.ts await chrome.offscreen.createDocument({ url: "src/offscreen.html", reasons: ["AUDIO_PROCESSING"], });
-
Immediately open
chrome://extensions/and inspect it
// In offscreen document DevTools console:
// Check AudioContext state
const audioContext = new AudioContext();
console.log("AudioContext state:", audioContext.state);
// Test audio processing
const audioBuffer = audioContext.createBuffer(1, 44100, 44100);
console.log("Audio buffer created:", audioBuffer.duration, "seconds");
// Monitor messages from service worker
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
console.log("Offscreen received:", message);
sendResponse({ status: "processed" });
});"Offscreen document already exists"
// In background.ts, prevent duplicate creation:
chrome.offscreen.hasDocument().then((hasDocument) => {
if (!hasDocument) {
chrome.offscreen.createDocument({
url: "src/offscreen.html",
reasons: ["AUDIO_PROCESSING"],
});
}
});"Failed to create offscreen document"
- Ensure
offscreenpermission is inmanifest.json - Check that offscreen.html exists in src/
The side panel (dashboard, src/dashboard.ts) shows:
- Meeting summary and transcript
- Participant list
- Settings and configuration
- Open the side panel by clicking the Late Meet extension icon
- Right-click in the side panel → Inspect
- DevTools opens for the side panel context
- Go to
chrome://extensions/ - Click Details for Late Meet
- Scroll down to Extension Pages
- Click inspect next to
dashboard.html
// In side panel DevTools console:
// Check if side panel received data from service worker
chrome.runtime.sendMessage({ type: "GET_TRANSCRIPT" }, (response) => {
console.log("Received transcript:", response);
});
// Monitor state changes
chrome.storage.local.get(null, (data) => {
console.log("Current side panel data:", data);
});
// Test UI interactions
const button = document.querySelector("button[data-action]");
button?.click();
console.log("Button clicked");The side panel state is stored in chrome.storage.local:
// Save state
chrome.storage.local.set({
dashboardState: {
activeTab: "transcript",
scrollPosition: 0,
},
});
// Retrieve state
chrome.storage.local.get("dashboardState", (data) => {
console.log("Dashboard state:", data.dashboardState);
});| Error | Cause | Solution |
|---|---|---|
| "Content script blocked by CSP" | Content Security Policy violation | Check manifest.json permissions; verify script-src allows unsafe-inline for Manifest V3 |
| "Service worker crashed" | Exception in background.ts | Open chrome://serviceworker-internals/ → inspect → check console for errors |
| "Cannot read property 'sendMessage' of undefined" | chrome.runtime not available in page context | Use content script to relay messages from page to service worker |
| "Extension error: Could not establish connection" | Service worker not responding | Reload extension at chrome://extensions/; check if service worker is active |
| "Offscreen document not found" | Offscreen.html deleted or manifest incorrect | Verify offscreen.html exists in src/; check manifest permissions |
| "Port error: Could not establish connection" | Service worker terminated; message listener missing | Add message listener in service worker; restart service worker |
| "Cannot access Meet.google.com from content script" | Incorrect manifest permissions | Verify manifest has "host_permissions": ["https://meet.google.com/*"] |
| "AudioContext not available" | Missing offscreen document setup | Create offscreen document before using AudioContext in background |
| "Storage quota exceeded" | Too much data in chrome.storage.local | Implement cleanup; verify data sizes with chrome.storage.local.getBytesInUse() |
| "Popup closed immediately" | Error in popup.ts initialization | Open DevTools on popup before it closes; check popup.html console |
Extension not loading:
- Run
npm run buildornpm run dev - Navigate to
chrome://extensions/ - Click Load unpacked and select
dist/folder - Check Errors section on extension card
Features not working in Meet:
- Reload extension:
chrome://extensions/→ reload icon - Reload Meet tab (F5)
- Check Chrome version matches extension requirements
- Open content script DevTools and check console for errors
Storage issues:
// Clear all extension storage (CAUTION: destructive)
chrome.storage.local.clear();
chrome.storage.sync.clear();The most efficient way to develop and test your changes is using the hot reload workflow.
-
Start Vite in watch mode:
npm run dev
This rebuilds the extension automatically when you save files.
-
Load unpacked extension:
- Go to
chrome://extensions/ - Click Load unpacked
- Select the
dist/folder
- Go to
- Edit
src/content.ts - Vite rebuilds (automatic)
- Reload the Meet tab (F5)
- Changes appear immediately
# Terminal window 1: Vite watch
npm run dev
# Terminal window 2: In src/ directory
# Make changes to content.ts
# Reload Meet tab to see changes- Edit
src/background.ts - Vite rebuilds (automatic)
- Go to
chrome://extensions/ - Click reload icon under Late Meet
- Changes active immediately
- Edit
src/popup.ts,src/dashboard.ts, or styles - Vite rebuilds (automatic)
- Click the extension icon or re-open the side panel
- Changes appear immediately
Terminal Setup (Recommended):
# Terminal 1: Run Vite
npm run dev
# Terminal 2: Run linter (optional, for real-time feedback)
npm run lint -- --watch
# Terminal 3: Run tests (if working on test files)
npm testVS Code Setup (Recommended):
- Install Thunder Client or REST Client for API testing
- Use Chrome DevTools in separate windows for each component:
- Service worker:
chrome://serviceworker-internals/ - Content script: DevTools on Meet tab
- Side panel: DevTools on side panel
- Service worker:
- Keep manifest.json open for quick reference
| Practice | Benefit |
|---|---|
| Open DevTools before saving changes | See errors immediately as Vite rebuilds |
Use console.log() with clear prefixes |
[ContentScript], [ServiceWorker], [Dashboard] |
| Keep changes small and focused | Easier to debug when something breaks |
Run npm run lint after finishing a feature |
Catch issues early |
| Test on actual Google Meet instance | Catch real-world issues; use test meet at meet.google.com/new |
Changes not appearing after save?
# Manually rebuild
npm run build
# Or if using dev mode, restart Vite
# Ctrl+C to stop, then npm run devBuild succeeds but extension doesn't update?
- Go to
chrome://extensions/ - Click reload icon under Late Meet
- If still not working, unload and reload:
- Toggle extension off, then back on
- Or remove and re-add from
dist/folder
// In background.ts
console.log("[ServiceWorker]", "Meeting state:", state);
// In content.ts
console.log("[ContentScript]", "Participants updated:", participants);
// In dashboard.ts
console.log("[Dashboard]", "Transcript loaded:", transcript);- Windows/Linux:
Ctrl + Shift + J(DevTools console) - Mac:
Cmd + Option + J
Create a test HTML file to test UI components in isolation:
<!-- test.html -->
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="src/theme.css" />
</head>
<body>
<div id="test-container"></div>
<script type="module">
import { initDashboard } from "./src/dashboard";
initDashboard(document.getElementById("test-container"));
</script>
</body>
</html>Service worker makes API calls to OpenAI and ElevenLabs. Monitor them:
- Open
chrome://serviceworker-internals/ - Inspect service worker
- Open Network tab in DevTools
- Perform action that makes API call
- Check request/response
- GitHub Issues: Open an issue
- Contributing Guide: See CONTRIBUTING.md
- Chrome Extension Docs: Official Manifest V3 Docs
- Security & Privacy: See SECURITY.md
Happy developing! 🚀