Skip to main content

Architecture Overview

Knowledge Tooltip is built as a Chrome Extension using Manifest V3, the latest Chrome extension platform. The architecture follows Chrome’s security model with clear separation between UI and network operations.

Core Architecture Components

The extension consists of three main components:

1. Content Script (content.js)

Role: User interface and interaction layer
  • Injected into every webpage (<all_urls>)
  • Handles text selection events
  • Manages tooltip UI rendering
  • Processes user interactions (tabs, buttons, inputs)
  • Cannot make cross-origin API calls due to CORS
The content script runs in an isolated context separate from the webpage’s JavaScript, providing security while allowing DOM manipulation.

2. Background Service Worker (background.js)

Role: API gateway and network handler
  • Runs as a persistent service worker
  • Handles all external API requests
  • Bypasses CORS restrictions using Chrome extension privileges
  • Routes requests to Wikipedia, Wiktionary, Wikidata, and OpenAI
  • Manages API key storage (local storage for security)
Background service workers in Manifest V3 can idle when not in use, improving performance and resource usage compared to persistent background pages in Manifest V2.

3. Manifest Configuration (manifest.json)

Role: Extension configuration and permissions Key configuration elements:
manifest.json
{
  "manifest_version": 3,
  "permissions": [
    "activeTab",
    "storage"
  ],
  "host_permissions": [
    "https://en.wikipedia.org/*",
    "https://ar.wikipedia.org/*",
    "https://en.wiktionary.org/*",
    "https://ar.wiktionary.org/*",
    "https://api.dictionaryapi.dev/*",
    "https://www.wikidata.org/*",
    "https://api.openai.com/*"
  ],
  "background": {
    "service_worker": "background.js"
  },
  "content_scripts": [
    {
      "matches": ["<all_urls>"],
      "js": ["content.js"],
      "css": ["styles.css"],
      "run_at": "document_idle"
    }
  ]
}
The host_permissions array explicitly declares all external APIs the extension will access, providing transparency to users and enabling CORS bypass.

Why This Architecture?

Security Model

Manifest V3 enforces strict separation of concerns:
  • Content scripts cannot access sensitive APIs or make unrestricted network calls
  • Background workers have elevated privileges but no direct DOM access
  • Message passing provides controlled communication between isolated contexts

CORS Workaround

Browser security prevents webpages (and content scripts) from making cross-origin API requests. The background worker solves this:
  1. Content script sends a request message
  2. Background worker makes the actual HTTP call (CORS doesn’t apply)
  3. Background worker returns the data to content script
This pattern is the recommended Manifest V3 approach for extensions that need to fetch data from multiple external APIs.

Data Flow Example

When a user highlights “Quantum Computing”:
1. User selects text → content.js detects mouseup event
2. content.js displays tooltip button
3. User clicks button → content.js creates tooltip shell
4. content.js sends: chrome.runtime.sendMessage({
     action: 'fetchWikipedia',
     term: 'Quantum Computing',
     language: 'en'
   })
5. background.js receives message, calls Wikipedia API
6. Wikipedia returns JSON → background.js forwards to content.js
7. content.js renders summary in tooltip UI

Key Architectural Decisions

Local-First Storage

  • chrome.storage.sync: User preferences (enabled state, language)
  • chrome.storage.local: Sensitive data (OpenAI API key)
  • In-memory cache: API responses (Map with LRU eviction)

Modular Tab System

Each tab (Summary, Define, Facts, AI, Translate) follows a consistent pattern:
// Tab loading flow
function loadTabContent(tabId) {
  const cached = getFromCache(term, tabId);
  if (cached) {
    renderTabContent(tabId, cached, contentArea);
    return;
  }
  
  // Show loading state
  contentArea.appendChild(createLoadingIndicator());
  
  // Fetch via background worker
  chrome.runtime.sendMessage({
    action: `fetch${TabName}`,
    term: term,
    language: currentLanguage
  }).then(response => {
    setCache(term, tabId, response.data);
    renderTabContent(tabId, response.data, contentArea);
  });
}

Performance Optimizations

  • Debouncing: Text selection events debounced by 100ms
  • Caching: LRU cache (30 entries max) prevents redundant API calls
  • Lazy loading: Tab content only fetched when tab is activated
  • Service worker idling: Background worker auto-sleeps when inactive

Next Steps

Content Script

Deep dive into UI layer and event handling

Background Worker

API routing and CORS bypass implementation

Message Passing

Communication protocol between components

Build docs developers (and LLMs) love