Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/alexperezortuno/ce-blocker/llms.txt

Use this file to discover all available pages before exploring further.

Traffic Blocker is built on a two-process architecture mandated by Chrome’s Manifest V3 platform. A Vue 3 single-page application runs as the popup and handles all user interaction, while a background service worker handles counting blocked requests and initialising storage on first install. Rule updates reach declarativeNetRequest through two paths: the Dashboard popup calls declarativeNetRequest directly for fast inline edits, while the Settings component routes preset and import operations through chrome.runtime.sendMessage to the background service worker.

Directory Structure

src/
├── main.ts                  # Popup entry (Vue app + router)
├── App.vue                  # Root component (dark mode state)
├── vite-env.d.ts            # Vite environment type declarations
├── background/
│   └── background.ts        # Background service worker
├── components/
│   ├── Dashboard.vue        # Main view — add/edit/delete/search/move rules, toggle
│   ├── PageNotFound.vue     # Catch-all 404 route component
│   ├── Settings.vue         # Dark mode, export/import, presets
│   └── Statistics.vue       # Blocked traffic counter + reset
└── scss/
    └── style.scss           # Global SCSS styles
main.ts bootstraps a Vue 3 application and mounts it to #app inside index.html. The app uses vue-router with createWebHistory and three content routes plus a catch-all:
PathNameComponent
/DashboardDashboard.vue — add, edit, delete, search, reorder rules, and toggle blocking
/statisticsStatisticsStatistics.vue — blocked traffic counter with reset action
/settingsSettingsSettings.vue — dark mode toggle, export/import, and preset blocklists
App.vue is the root component and owns the isDarkMode reactive ref. On mount it reads settings.darkMode from chrome.storage.local and applies (or removes) the dark-mode CSS class on document.body. It exposes isDarkMode to child routes via defineExpose and listens for a toggle-dark-mode event emitted up through the <router-view>:
<router-view :isDarkMode="isDarkMode" @toggle-dark-mode="isDarkMode = !isDarkMode"/>

Background Service Worker

The background script is registered in manifest.json as:
{
  "background": {
    "service_worker": "assets/background.js"
  }
}
It registers three event listeners at startup inside an immediately-invoked registerEventListeners() call:
  • chrome.runtime.onInstalled — fires on first install. Reads both settings and blocker keys from chrome.storage.local. If settings is absent it writes { blocked: 0 }. If blocker is absent it writes { rules: [], isEnabled: false }.
  • chrome.webRequest.onErrorOccurred — attached to <all_urls> with the extraHeaders option. Each time a request fails with res.error === 'net::ERR_BLOCKED_BY_CLIENT' it reads the current settings.blocked value from storage and increments it by one.
  • chrome.runtime.onMessage — handles updateRules messages from the popup. A module-level isUpdatingRules boolean flag prevents concurrent updates from racing. When a valid message arrives the background auto-fixes and re-indexes every rule, fetches the current dynamic-rule IDs, removes them all, then adds the new set (or an empty array when blocking is disabled). Finally it persists the new state to chrome.storage.local and replies to the popup.

Communication Between Popup and Background

When the Settings component applies a preset or imports rules, it delegates to the background via chrome.runtime.sendMessage:
// Message sent by Settings.vue → chrome.runtime.sendMessage
interface UpdateRulesMessage {
  updateRules: {
    data: Rule[];       // full current rule list
    isEnabled: boolean; // whether blocking should be active
  };
}

// Response from the background service worker
interface UpdateRulesResponse {
  success: boolean;
  error?: string; // present only on failure, e.g. "Update in progress"
}
The listener returns true to keep the message channel open for the asynchronous sendResponse call, which is required by the Chrome runtime when responding after an await or callback boundary.

Data Flow

Dashboard inline rule edits

When a user adds or edits a rule directly in the Dashboard, updateStaticRules() handles everything inside the popup:
1

User submits a rule

The Dashboard component validates the input and pushes the new rule into the local rules array.
2

Popup calls updateStaticRules()

The helper builds a cleaned rule list with sequential IDs starting from 1, then calls declarativeNetRequest directly from the popup context.
3

declarativeNetRequest.updateDynamicRules applies the change

All previous dynamic rules are removed by ID, then the new rules are added (or an empty array when isEnabled is false).
4

chrome.storage.local.set persists state

The popup writes { blocker: { rules, isEnabled } } to local storage directly, completing the update without a background round-trip.

Settings-driven rule updates (presets and imports)

When the Settings component applies a preset blocklist or imports rules from a file, it routes the update through the background service worker:
1

Settings sends an updateRules message

chrome.runtime.sendMessage({ updateRules: { data: rules, isEnabled } }) dispatches to the background and awaits the response.
2

Background validates and auto-fixes

The service worker runs each rule through autoFixRule(), reassigning sequential IDs starting from 1.
3

declarativeNetRequest.updateDynamicRules applies the change

All previous dynamic rules are removed by ID, then the new set is added (or an empty array when isEnabled is false).
4

Background persists state and responds

The background writes { blocker: { rules, isEnabled } } to local storage, then sends { success: true } back to the popup.

Build docs developers (and LLMs) love