Skip to content

Data Model

Lifecycle Tab

The core data structure. Every snoozed, queued, or watched tab is stored as a LifecycleTab:

interface LifecycleTab {
  id: string;              // UUID
  url: string;             // http/https only
  title: string;
  favIconUrl?: string;
  state: LifecycleState;   // "snoozed" | "queued" | "watching"
  createdAt: number;       // Unix timestamp (ms)
  originWindowId: number;  // Chrome window ID for restoration

  // Snoozed state
  wakeAt?: number;         // Unix timestamp when alarm fires

  // Queued state
  position?: number;       // Queue order (0-based)

  // Watching state
  cssSelector?: string;    // CSS selector for watched element
  pollIntervalMinutes?: number;  // Default: 30
  lastCheckedAt?: number;  // Last poll timestamp
  lastContentHash?: string; // SHA-256 of element textContent
  changedAt?: number | null; // When change was detected

  // Meeting mode
  meetingId?: string;      // Groups tabs snoozed together
}

State Transitions

stateDiagram-v2
    Active --> Snoozed: snoozeTab()
    Active --> Queued: queueTab()
    Active --> Watching: watchTab()
    Snoozed --> Active: wakeTab() / alarm fires
    Queued --> Active: wakeTab() via getNextQueuedTab()
    Watching --> Active: wakeTab() on change
    Snoozed --> [*]: removeTab()
    Queued --> [*]: removeTab()
    Watching --> [*]: removeTab()

All transitions go through the lifecycle-manager module, which holds a mutex to prevent concurrent storage corruption.

Storage Layout

Chrome Storage (chrome.storage.local)

{
  "lifecycle": [LifecycleTab, ...],
  "meetingMode": { "active": false, "meetingId": null },
  "attention": { "snoozesWoken": ["id1"], "watchesChanged": ["id2"] },
  "config": { ExtensionConfig }
}

Bridge File Storage (~/.tab-manager/)

~/.tab-manager/
  config.json          # Server + extension config
  active-tabs.json     # Last synced active tabs
  lifecycle-tabs.json  # Lifecycle state (mirror of Chrome Storage)
  bridge.pid           # PID file for port management
  bridge.port          # Current port number

Extension Config

interface ExtensionConfig {
  thresholds: { warning: number; alert: number };
  staleMinutes: number;
  bridgeUrl: string;
  defaultPollIntervalMinutes: number;
  defaultSnoozeDurationMinutes: number;
}

Tracked Tab (Active)

Active tabs are tracked in-memory by the service worker:

interface TrackedTab {
  id: number;           // Chrome tab ID
  windowId: number;
  url: string;
  title: string;
  favIconUrl?: string;
  pinned: boolean;
  groupId: number;
  index: number;
  lastAccessed: number; // In-memory only, resets on SW restart
  createdAt: number;
}

Ephemeral tracking

lastAccessed and createdAt for active tabs reset when the service worker restarts (MV3 service workers are ephemeral). This affects stale-tab detection accuracy but has no impact on lifecycle state, which is persisted in Chrome Storage.