Blincr

Documentation/ Blincr/ blincr

Chrome extension

Content script event capture, background service worker, popup, and side panel architecture.

Article Slug: extension Updated: 27 Jun 2026

Extension structure

extension/
  manifest.json          MV3 manifest (permissions, entry points)
  build.js               esbuild bundler (ESM for bg/popup/sidepanel, IIFE for content)
  src/
    config.ts            API_BASE, WS_BASE, storage keys, scroll throttle
    types.ts             BrowseEvent, SessionStatus, RuntimeMessage, overlay types
    content.ts           Injected into every page — event capture + replay
    background.ts        Service worker — session management, WebSocket, replay engine
    popup.ts             Extension popup UI (Session, Trails, About tabs)
    sidepanel.ts         Side panel — live trail tree + library manager
    trail-db.ts          IndexedDB wrapper for local trail CRUD
    plugins/
      overlay-manager.ts Plugin registry, overlay state, positioning, drag, SPA nav
      comment.ts         Comment plugin — render, edit, delete, 5 style themes
  popup/
    popup.html           Popup markup
    popup.css            Popup styles
  sidepanel/
    sidepanel.html       Side panel markup
    sidepanel.css        Side panel styles
  dist/                  Built bundles (.gitignored)

Manifest permissions

Content script (content.ts)

Injected as IIFE into every page. Uses a __blincr_injected guard to prevent double injection. Captures seven event types:

The content script also handles replay: applyReplayEvent() processes incoming click, scroll, and overlay events from viewers, using a cascade of selector strategies (exact selector, data-testid, ARIA label, text content match) to find target elements.

Overlay plugin system (plugins/)

A generalized architecture for placing persistent UI components on web pages, tracked as trail events and visible during replay.

Plugin interface (OverlayPlugin): Each plugin provides render(), update(), remove(), and createInputUI(). Plugins are registered during initBlincr() via registerPlugin(). The registry is a compile-time pattern — esbuild bundles plugin imports into the IIFE, so no dynamic loading is needed.

Overlay manager (plugins/overlay-manager.ts): Sits between the plugin registry and the DOM. Manages:

Event shape: A single "overlay" event type with a pluginId discriminator in the payload:

payload: {
  pluginId: "comment"
  action: "create" | "update" | "delete"
  overlayId: UUID (stable across CRUD operations)
  url: page URL
  position: { strategy, vpX, vpY, selector?, offsetX?, offsetY? }
  data: plugin-specific (e.g. { text, style, authorName, authorId })
}

CRUD is append-only: create/update/delete are separate events pointing to the same overlayId. The background materializes current state by replaying events in order. During trail replay, overlay events are forwarded to the content script's handleOverlayEvent() which delegates to the plugin's render/update/remove.

Comment plugin (plugins/comment.ts)

First overlay plugin instance. Provides styled, draggable comment bubbles on any web page.

Activation: Alt+Click on any page element or empty space. Also available via "Add Comment" button in the popup (sends ACTIVATE_COMMENT_MODE to the content script).

Creation flow: Alt+Click shows an inline form with a textarea, a style picker (5 colored circles), and Cancel/Post buttons. Enter submits, Shift+Enter for newlines, Escape cancels.

5 visual themes:

Editing: Click the edit button (✎) to transform the comment body into an inline textarea. Enter saves, Escape reverts.

Deletion: Click the delete button (✕) to emit a delete event. The overlay fades out over 200ms.

Drag: The comment header is a drag handle. Mousedown starts drag mode, mousemove repositions, mouseup emits an update event with the new viewport-relative position.

Background service worker (background.ts)

Central orchestrator running as a Chrome service worker. Key responsibilities:

Session lifecyclecreateSession() POSTs to the backend, opens a WebSocket, sets the badge to green "ON", and injects the content script into the active tab. stopSession() saves the trail to IndexedDB, closes the WebSocket, clears alarms, and resets state. joinSession() connects as a viewer.

WebSocket relay — Maintains a persistent WebSocket to the SessionCoordinator DO. Queues events when disconnected. Exponential backoff reconnect (2^n seconds, max 30s) via chrome.alarms.

Event routing — Content script events arrive via chrome.runtime.onMessage with kind: "BROWSE_EVENT". The background enriches them with timestamp and participant ID, appends to the local trail, broadcasts to side panel ports, and sends over the WebSocket.

Replay engine — Full replay system with play/pause/stop, prev/next, skip-to-next-site, speed control (1x/2x/4x), and seek. Opens a dedicated replay tab and sends events to the content script for visual replay. Detects stale pages by comparing structural fingerprints.

IndexedDB bridge — Exposes all trail-db.ts operations via message passing (DB_LIST_TRAILS, DB_GET_TRAIL, DB_DELETE_TRAIL, DB_EXPORT_TRAIL, etc.) so the popup and side panel can access IndexedDB through the service worker context.

Side panel communication — Maintains an array of connected blincr-sidepanel ports. Broadcasts trail events, sync messages, replay state, and stale page warnings.

Popup (popup.ts + popup.html)

Three-tab popup accessible from the extension icon:

Side panel (sidepanel.ts + sidepanel.html)

Two-view side panel opened from the popup or Chrome's side panel toggle:

Live view — Hierarchical domain-grouped trail tree with expandable domains and pages. Shows event counts, timestamps, and favicon icons. Includes a full replay control bar (play/pause, prev/next, next-site, speed selector, scrubber, waypoint jump dropdown). Stale page detection toasts with skip/dismiss actions.

Library view — Lists all locally saved trails as cards with sync status badges (local/synced/modified), domain favicons, tags, and folder labels. Supports search, folder filtering, and bulk export/import. Trail detail view with editable title/tags/description, tree preview, and actions: replay, export (.blincr), upload to server, delete.

Trail storage (trail-db.ts)

IndexedDB database blincr-trails with two object stores:

Operations: saveTrail, getTrail, deleteTrail, listTrails, searchTrails, updateTrailMeta, removeEventsFromTrail, removePageFromTrail, splitTrail, mergeTrails, exportTrail, importTrailData, exportAllTrails, importBackup, getTrailCount.

Auto-title generation: single domain shows "Trail on example.com", multiple domains are comma-joined, 4+ domains show "a.com, b.com +2 more".