Skip to content

Latest commit

 

History

71 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Tabs Rotator / Slideshow

A lightweight Chrome/Chromium extension that cycles through a list of URLs in separate tabs like a slideshow — perfect for dashboards, monitoring screens, TVs, info-kiosks, digital signage, reports, presentations, and more.


Table of Contents


Features

  • Rotate through web pages from the Internet or local files (file:// links).
  • Per-page display time and auto-reload interval.
  • Manage configuration directly in the extension UI or use import/export.
  • Remote configuration URL with automatic periodic updates (host JSON anywhere).
  • Built-in retry and temporary skip for failing pages.
  • Persistence across browser restarts without tab spam.
  • Offline-friendly: keeps the last good version if a page update fails.
  • Optional Prevent window focus mode (keeps Chrome in background while rotating)
  • Rotation watchdog self-heal (recovers if a rotate alarm is missed)
  • Diagnostics panel with live state, timestamps & force actions
  • One-click Force rotate now button for manual advancement
  • Activation diagnostics & health badge (history, last success age, color-coded badge)
  • Debug activation logging toggle (enable deep rotation logging only when you need it)

Screenshots:

Local Configuration Remote Configuration


Quick Start

  1. Install the extension (Chrome/Edge/Brave supported).
  2. Open the extension by clicking on it’s icon, then set Local Configuration or choose Remote Config (sample https://api.jsonsilo.com/public/e683a7af-7366-4db0-94fe-3438c9f64092).
  3. Add your pages (URLs) with a display time and optional reload interval.
  4. Start rotating (open the rotator and press Start). Press F11 to go fullscreen or set Start in Fullscreen Mode in configuration.
  5. To stop rotation, first exit from the full screen mode by pressing F11, then click on the extension icon and click Stop.

Local files? In chrome://extensions/Tabs Rotator → enable Allow access to file URLs to use file:// links.


Usage

Local Configuration

Configure pages directly inside the extension UI. You can export to JSON and import later or on another device.

Remote Configuration (Auto-Update)

Host a JSON file (JSON Silo, n:point, Pantry, S3, GitHub Pages, Gist, etc.) and paste its URL in the options. The extension fetches it on a schedule and applies updates without reinstalling.

  • Configuration URL: HTTPS or file:// is supported.
  • Reload Interval (minutes): Set to 0 to disable periodic refresh of the remote file.

A small public example is here:
tabs-rotator-config.json

Page Options

Name Description JSON Key Type
URL The page to display. https://… or file://…. url String
Display Time Time in seconds the page stays visible before switching. delaySeconds Number
Reload After Page reload interval in seconds. Use 0 to disable reloading. reloadIntervalSeconds Number

OS Autostart (Windows)

For automatic startup of Chrome on sign-in, follow the short guide: Windows Autostart (Chrome) — Windows 10/11.


Configuration Examples

Local / Inline

{
  "pages": [
    {
      "url": "https://status.example.com",
      "delaySeconds": 20,
      "reloadIntervalSeconds": 60
    },
    {
      "url": "file:///C:/dashboards/report.html",
      "delaySeconds": 15,
      "reloadIntervalSeconds": 0
    },
    {
      "url": "https://grafana.example.com/d/sales",
      "delaySeconds": 30,
      "reloadIntervalSeconds": 3600
    }
  ],
  "isFullscreen": true,
  "preventWindowFocus": true
}

Remote (host this JSON and point the extension to it)

{
  "pages": [
    { "url": "https://example.com/one", "delaySeconds": 15, "reloadIntervalSeconds": 0 },
    { "url": "https://example.com/two", "delaySeconds": 20, "reloadIntervalSeconds": 120 }
  ],
  "isFullscreen": true,
  "preventWindowFocus": false
}

Advanced Settings

These optional controls help tune behavior for signage / unattended scenarios:

  • Prevent window focus (preventWindowFocus): When enabled, tab rotation won’t bring the Chrome window to the foreground. Useful if you’re using the machine for something else while a dashboard rotates in the background (e.g. on a secondary display).
  • Start in Fullscreen Mode (isFullscreen): Automatically requests fullscreen (F11 equivalent) after tabs are created.
  • Remote Reload Interval: Defines how often (in minutes) the remote JSON config is re-fetched. Set to 0 to disable automatic refresh but still perform an initial load.
  • Force rotate (Diagnostics): Manually advances to the next page immediately; handy for testing or if you just updated a page.
  • Watchdog Self-Heal: Internal mechanism that restarts rotation if the scheduled alarm was missed (no UI toggle; always on).
  • reuseLocalFileTabs (reuseLocalFileTabs): When true, file:// pages use legacy in-place reload (no hidden preload). When false (default / omitted) they behave like normal web pages and get a hidden preload tab first for faster promotion of heavy local dashboards.

Minimal example with advanced keys:

{
  "pages": [{ "url": "https://example.com/dashboard", "delaySeconds": 20, "reloadIntervalSeconds": 300 }],
  "isFullscreen": false,
  "preventWindowFocus": true,
  "reuseLocalFileTabs": false
}

Tips & Troubleshooting

  • Local files don’t load (file://)
    Enable Allow access to file URLs for the extension under chrome://extensions/. To opt back into the older in-place reload behavior for local files, add "reuseLocalFileTabs": true to your configuration JSON.

  • Some sites log out after a while
    Use the site’s "keep alive"/auto-refresh, increase per-page reloadIntervalSeconds, or keep a logged-in session.

  • A page fails to load
    The rotator will retry and temporarily skip failing pages. Check DevTools (F12) → Console for errors.

  • Fullscreen / Kiosk
    Set "Start in Fullscreen Mode" in configuration. For signage, consider Chrome’s kiosk mode or an OS auto-start to open the browser and extension on boot.

  • Tabs keep multiplying
    The extension persists state across restarts and prevents tab spam and removes all opened tabs on stop. If something seems off, stop rotation, close tabs, and start again.

  • Need to advance immediately
    Open Diagnostics → click Force rotate to move to the next page instantly.

  • Rotation stalled / stopped at last tab
    The watchdog typically restarts it automatically. Open Diagnostics to confirm timestamps. Use Force rotate if needed. Supports activation history + badge color to help spot issues.


Privacy & Permissions

  • Uses only the minimal permissions necessary to open/rotate tabs and store your settings locally.
  • No analytics, tracking, or external calls unless you opt-in by configuring a Remote Configuration URL.
  • Remote configs are fetched read-only; the extension never writes data back to remote services.

Development

End-to-End (E2E) Crash / Preservation Test

Negative (Non-Preserve) Scenario

We also test the inverse: when auto-preserve is explicitly disabled we expect the simulated restart to recreate rotation tabs (new tab IDs). The spec e2e/tests/non-preserve-reload.spec.ts calls:

__e2eApi.disableAutoPreserve();
// then invokes simulateServiceWorkerRestart(context)

Negative test support API:

Method Purpose
disableAutoPreserve() Sets DisableAutoPreserveNextInit so next init does not preserve existing tabs

Preserve logic summary:

Storage flag Effect
forcePreserveNextInit Internal flag: if true (and disable flag not set) next initialize() uses preserveExisting
disableAutoPreserveNextInit Internal flag set by disableAutoPreserve(); next init will not preserve tabs

In production builds (NODE_ENV=production) the entire __e2eApi block is stripped by the webpack DefinePlugin guard (__E2E_TESTING__ becomes false), reducing bundle surface area.

Playwright exercises a full extension lifecycle, including a simulated MV3 service worker restart ("crash") while preserving existing rotation tabs. Real MV3 runtime reload detection in headless Chromium is unreliable; the test harness uses simulateServiceWorkerRestart(context) to re-run rotationService.initialize with the appropriate preservation option instead of relying on a new Worker instance.

Scripts:

Command Purpose
yarn e2e Build extension then run headless Playwright tests
yarn e2e:headed Run tests in headed Chromium
yarn e2e:debug Launch with Playwright inspector (PWDEBUG)

First time only install the browser (if not already):

npx playwright install chromium

Key test files:

  • e2e/tests/crash-recovery.spec.ts: preserved resume scenario
  • e2e/tests/non-preserve-reload.spec.ts: non-preserve negative scenario

Helper: simulateServiceWorkerRestart(context) in e2e/utils/launch-extension.ts.

E2E Execution

The E2E Playwright tests are intended for local development only and are not executed in CI (CI builds and runs unit tests + lint + coverage). This avoids flakiness with MV3 extension loading in headless environments.

Run locally (headed Chromium):

yarn e2e

Or with the inspector for debugging:

yarn e2e:debug

Evaluate Harness (__e2eApi)

For reliability we bypass chrome.runtime.sendMessage and talk directly to the service worker global via page.serviceWorker().evaluate(...). The background script exposes a non-production API object:

__e2eApi = {
  startWithConfig(config),
  getDiagnostics(),
  getState(),
  listTabs(),
  forceHeartbeat(),
  adoptTabs(),
  // crash() was replaced by simulateServiceWorkerRestart(context) harness helper for reliability (MV3 reload detection is flaky headless)
}

ForcePreserveNextInit is a storage flag that causes the next initialize() call after a worker reload to adopt existing tab IDs instead of recreating tabs. The crash recovery test asserts that tab IDs survive across a simulated reload.

Adding New E2E Scenarios

  1. Create a spec in e2e/tests/*.spec.ts.
  2. Launch via one of the scripts above.
  3. Use serviceWorker.evaluate to call into __e2eApi.

Current E2E Scenario Coverage (Playwright)

The suite exercises rotation progression, reload cadence, fullscreen/focus modes, error recovery & retries, remote config reload, import/export, preservation boundaries, crash / non‑preserve restart, tab adoption and pruning, startup race handling, watchdog self‑heals (stall + missing alarms), cleanup stop path, metrics/large‑scale behavior, cross‑window isolation, storage failure resilience, and grace period enforcement. Heavy scenarios (scale, metrics snapshot growth, cross‑window) are tagged with @heavy and executed in a single‑worker project to reduce resource spikes.

Reliability helpers (in e2e/utils/reliability-helpers.ts) eliminate flakiness by waiting on explicit internal signals rather than arbitrary sleeps:

waitForWorkerApi(context)        // service worker & __e2eApi ready
waitForTabIds(context, count)    // rotation tabs created
waitForIndex(context, idx)       // current index observed
waitForRotationCycle(context, n) // rotation cycle counter reached
pollAlarms(context)              // list current alarm names

Use these instead of manual polling loops for consistency.

Harness API (__e2eApi)

Minimal non‑production test surface is exposed behind a build flag. It lets tests start rotation, inspect diagnostics/state, advance indices, simulate errors, adjust watchdog/grace timing, and export/update config. For exact method names see the background script (__e2eApi definition). Production builds strip this entire block.

Troubleshooting

Symptom Tip
Test hangs waiting for rotation Ensure delaySeconds >= 3 (validator) and config uses StorageKeys.LocalConfig.
Tabs not preserved after crash Confirm forceHeartbeat(), adoptTabs(), then crash() were invoked; check resumeReason in diagnostics.
SW not ready Open popup chrome-extension://<id>/index.html or poll for __e2eApi.

Type Checking

Run a full multi-target type check:

yarn typecheck

This validates app code (tsconfig.app.json), background code (tsconfig.background.json), and harness (tsconfig.harness.json). E2E tests compile via Playwright + their own tsconfig (tsconfig.e2e.json).

Requirements: Node.js ≥ 18, npm and yarn.

  1. Install dependencies

    yarn
  2. Build the extension (produces a clean dist/ and a zip bundle)

    yarn build
  3. Run in watch mode for local development

    yarn start
  4. Load the unpacked extension

    • Open chrome://extensions/
    • Enable Developer mode (top-right)
    • Click Load unpacked and choose the subfolder inside dist/ created by the build

Tip: If you change background or options code, the service worker may need a manual reload in chrome://extensions/ during development.

Test Suite Overview (UI + Background)

Two layers:

  1. Background (Node + Jasmine) – rotation & tab logic.
  2. Angular UI (Karma + Jasmine) – component/service specs.

Background Tests

yarn test:bg

Watch:

yarn test:bg:watch

UI Tests

yarn test:ui

Watch:

yarn test:ui:watch

Combined

yarn test:all

Watch both:

yarn test:all:watch

Test Matrix (TypeScript Project / tsconfig Mapping)

Layer / Purpose tsconfig Script(s) Notes
Angular UI unit/shared tsconfig.spec.app.json yarn test:ui Karma + ChromeHeadless
Background rotation logic tsconfig.background.spec.json yarn test:bg Jasmine (Node, CommonJS build)
Playwright E2E (extension) tsconfig.e2e.json yarn e2e, yarn e2e:debug MV3 lifecycle + full integration
Production UI build tsconfig.app.json yarn build / ng build Excludes all *.spec.ts
Production background SW tsconfig.background.json Part of yarn build Excludes tests directory
Harness / manual scripts tsconfig.harness.json node dist/harness/*.js Exploratory / tab-manager harness
Tooling / configs tsconfig.tools.json (editor only, no emit) Playwright config + scripts

Naming Conventions:

  • Background tests use *.bg-spec.ts to avoid accidental production inclusion.
  • UI tests use *.spec.ts under src/app or src/shared.
  • E2E tests live under e2e/tests/*.spec.ts.

Adding a New Test:

  1. Choose layer (UI / background / E2E).
  2. Use correct suffix (.spec.ts or .bg-spec.ts).
  3. Run corresponding script.

If a test is not picked up:

  • Verify path matches its tsconfig include.
  • Confirm naming convention is correct.
  • For background, rebuild with yarn test:bg:build if needed.

Troubleshooting:

Symptom Cause Fix
No specs found (bg) Not compiled / wrong dir yarn test:bg:build then re-run
UI hang Headless Chrome missing Adjust script or install Chrome
chrome types leak Background file imported into UI Move shared models to src/app/models
Stale bg code Forgot rebuild Use yarn test:bg or watch

TypeScript Project Layout (Multi-Config)

The repository uses a multi-tsconfig layout to isolate concerns and remove accidental Chrome ambient types from Node / test contexts:

  • tsconfig.base.json – Shared compiler defaults (no implicit chrome types).
  • tsconfig.app.json – Angular UI build (extends base). Does not include chrome types unless UI code needs them.
  • tsconfig.background.json – Service worker / background scripts; adds types: ["chrome-types"] so the global namespace is available only here.
  • tsconfig.harness.json – Node harness for logic testing; only node types by default (can add a stub or chrome if needed).
  • Root tsconfig.json – Project references orchestrator so VS Code understands all sub-projects.

Benefits:

  • Faster, clearer editor IntelliSense (each file maps to an appropriate project).
  • Prevents leaking global chrome namespace into harness/tests accidentally.
  • Easier future migration or update when Chrome APIs change (using chrome-types).

If you add a new background file under src/background/, it is automatically picked up by tsconfig.background.json. For UI-only shared models, keep them in src/app/models/ so both app and background can import without pulling in Angular-specific code.

If you later need Chrome APIs in the UI (rare), add "types": ["chrome-types"] (or a minimal stub) to tsconfig.app.json.

Continuous Integration & Coverage

GitHub Actions workflow (.github/workflows/ci.yml) runs on pushes & pull requests targeting main.

Pipeline steps:

  1. Checkout & cache dependencies.
  2. yarn typecheck (background + UI TS integrity).
  3. yarn test:bg (background Jasmine specs).
  4. yarn test:ui (Karma/ChromeHeadless UI specs).
  5. Build & upload extension artifact (optional).
  6. Upload compiled background spec output for debugging.

Key scripts:

Script Purpose
yarn test Full suite (test:all).
yarn test:bg Background specs.
yarn test:ui UI specs.
yarn test:all Background then UI.
yarn test:bg:watch Watch background specs.
yarn test:ui:watch Watch UI specs.

Coverage (current status):

  • UI tests: Karma is configured; enable coverage via ng test --code-coverage (add a script like test:ui:coverage).
  • Background tests: Add NYC + source maps to instrument TS (e.g., nyc --reporter=lcov yarn test:bg).

Example UI coverage run:

ng test --watch=false --code-coverage

To add background coverage:

yarn add -D nyc source-map-support
nyc --reporter=lcov --reporter=text-summary yarn test:bg

Optionally merge coverage reports and upload to Codecov:

yarn install -D codecov
codecov -f coverage/lcov.info

Styling & Tailwind v4 Pipeline

The styling stack uses Tailwind CSS v4 directives with a split preflight file to avoid @import ordering warnings and centralize design tokens.

Core files:

File Purpose
src/tailwind.preflight.css Contains all Tailwind directives: @source, @theme tokens, @import "tailwindcss", @plugin, @tailwind utilities.
src/styles.source.css Authoring source: imports preflight then declares custom @utility classes and app base styles. Snapshot expands this.

Key directives:

  • @source – Replaces the old content array (scan paths for class extraction).
  • @theme {} – Inlined design tokens (colors, spacing, breakpoints, typography) used to generate utilities.
  • @plugin – Registers extra plugin layers (e.g. forms).
  • @tailwind utilities – Forces expansion of the utilities layer (explicit for some build chains).
  • @utility – Define bespoke one-off utilities (e.g. animate-spin-slow).

Linting:

stylelint is configured (see .stylelintrc.json) to allow Tailwind v4 at-rules: @source, @theme, @plugin, @tailwind, @utility.

Run CSS lint:

yarn lint:css

Extending design tokens:

  1. Edit the @theme block in src/tailwind.preflight.css.
  2. Add any new custom utility via @utility in src/styles.source.css.
  3. Re-run yarn start (watch) or yarn build to ensure new classes generate.

If your editor flags unknown at-rules, enable a Tailwind-aware extension; the build and Stylelint already accept them.

Remember to exclude out-tsc/ from instrumentation (instrument sources, not compiled output).

Manual Tailwind Snapshot (Optional)

The build uses a pre-generated Tailwind snapshot to ensure deterministic utilities. Directives are expanded by the prebuild step from src/styles.source.css (which imports tailwind.preflight.css) into src/styles.tailwind.css, which Angular consumes.

When you need to inspect or diff the fully expanded utilities (e.g. after upgrading Tailwind), generate a local snapshot:

yarn tailwind:generate

This writes src/styles.tailwind.css (gitignored & lint-ignored). Open it to see all emitted layers and utilities. Do not edit the snapshot directly—edit styles.source.css and re-run.


Changelog

For the full change log, open this file: Changelog.


Support

If you enjoy the project, a ⭐ on GitHub helps others find it!


Architecture

The extension is built as a Manifest V3 (service worker) + Angular UI hybrid. Modularized rotation logic into focused services to improve maintainability, testability, and observability.

Core Services (all under src/background/ unless noted)

  • rotation.service.ts – Thin coordinator (start/stop, delegations, high-level flow).
  • config.service.ts – Local + remote configuration loading & periodic refresh.
  • rotation-scheduler.service.ts – Computes/schedules next rotation alarm; starts countdown; updates health timing.
  • activation.service.ts – Resilient tab activation + diagnostics + focus orchestration hooks.
  • rotation-watchdog.service.ts – Missed-alarm / stall watchdog; triggers enforced rebuild + forced rotation.
  • startup-recovery.service.ts – Rehydrates state & alarms after MV3 service worker restart.
  • rotation-state.facade.ts – Canonical rotation state mutation (index advance, normalization, persistence, toolbar sync).
  • rotation-state.repository.ts – Persistence adapter for rotation state.
  • tab-manager.service.ts – Tab creation/tracking (primary + preloaded), allowed ID set, metrics logging.
  • tab-lifecycle.service.ts – Preload strategy + reload alarm handling + initial load wait.
  • invariant-rebuilder.service.ts – Idempotent rebuild & pruning of orphan/duplicate tabs.
  • stall-guard.service.ts – Stall heuristics (overdue rotation, cyclic last-page) and recovery signaling.
  • health-monitor.service.ts – Tracks timing & stall inputs → severity + badge color.
  • countdown.service.ts – Badge countdown timer overlay.
  • activation-diagnostics.service.ts – Activation attempt ring buffer & last success/error.
  • focus-orchestrator.service.ts – Fullscreen + optional focus suppression.
  • focus.service.ts – Low-level window focus attempt wrapper.
  • scheduler.service.ts – Low-level Chrome alarm helpers.
  • diagnostics.service.ts – Aggregates snapshot (tabs, timing, health, stalls, activation).
  • metrics.service.ts – Counter tracking + runtime broadcast.
  • storage.service.ts – Promise API + small TTL cache over chrome.storage.local.

rotation.service.ts deliberately stays thin; logic with its own state or heuristics lives in a dedicated service for clarity and testability.

Health & Badge System

Badge color summarizes overall rotation health, not just the last activation:

  • Inputs: timing (next due vs now), stalls, activation failure density, any recent success.
  • Severity: normal → warn (overdue or stall) → error (persistent failures / multiple stalls / no success yet).
  • Colors: green / amber / red; countdown time remains but is overridden by degraded severity.

Restart & Recovery Improvements

  • Rebuild is idempotent: only runs if tabsConfig absent (typical after MV3 worker restart) – avoids unnecessary tab enumeration.
  • Placeholder handling: missing tab IDs are lazily recreated on demand during rotation, preventing deadlocks at a single index.
  • Watchdog self‑heal: detects overdue rotations, triggers fast rebuild + invariant enforcement, and forces a near‑term rotate alarm.
  • Invariant enforcement centralized so stale / orphaned tabs are pruned without duplicating logic in rotation flow & watchdog.

Diagnostics Extensions

Diagnostics payload now includes:

  • severity & badgeColor (composite health view)
  • Activation history (recent attempts) & last activation success age
  • Stall metadata (count, reason, timestamps)
  • Normalized tracked vs. allowed tab IDs for spotting leaks

This richer snapshot enables a UI to present immediate actionable state without recomputing heuristics.

Rationale / Guiding Principles

  1. Single Responsibility – Each new service does one thing (preload/reload, focus, health, rebuild) → easier isolated tests.
  2. Crash/Restart Resilience – Service worker wakeups should not require reinitializing or duplicating logic; rebuild + invariant layers guarantee a safe baseline.
  3. Observability First – All critical transitions (activation attempt, stall, rotation schedule) feed diagnostics or metrics quickly, supporting live panels.
  4. Minimal Global State – Persistent state limited to RotationState + derived indexes; everything else is recomputable or ephemeral.

Supporting models live in src/app/models/ and remain framework-agnostic so they can be safely imported from the background service worker context.

Rotation Flow

  1. User clicks Start → RotationService.initialize() loads config, clears stale alarms, resets state.
  2. Tabs are created through TabManagerService (first tab active, others preloaded as needed).
  3. Each rotation cycle schedules the next alarm (rotate) based on the current page’s delaySeconds.
  4. On each alarm, RotationService.rotateTabs() activates the next tab (or swaps in a preloaded nextTab) and records timing.
  5. StallGuardService evaluates post-rotation and watchdog ticks; if a stall is detected, it triggers a controlled reset.
  6. DiagnosticsService composes real‑time status for the UI (Force Rotate, Enforce Now, etc.).

Tab Invariant Enforcement

To avoid “tab spam,” only up to 2 tabs per configured page (active + preloaded) are allowed. Extras (e.g. from restarts or failed preloads) are pruned during rotation and watchdog ticks. Ordering of tracked IDs is normalized so rebuilds correctly map page indices.

Focus Behavior

When preventWindowFocus is enabled, focus attempts are intentionally suppressed but still logged for diagnostics transparency. Fullscreen entry respects the setting (fullscreen without re‑focusing).

Extending Further

Potential next modular slices:

  • Persistence layer abstraction for storage calls
  • Metrics/event bus for streaming diagnostics updates
  • Optional in-memory cache layer atop StorageService for high-frequency reads

This architecture aims to keep RotationService as a thin coordinator while enabling isolated unit tests around logic-heavy concerns (tab lifecycle, stall detection, diagnostics shaping).

Metrics & Streaming Diagnostics

A lightweight MetricsService tracks counters:

  • rotations
  • stalls
  • focusAttempts
  • tabCreations
  • reloadsScheduled

Each mutation emits a runtime message { kind: 'metrics', event: { type, counters, at } } consumed by the diagnostics panel via a chrome.runtime.onMessage listener. This removes the need for high‑frequency polling; the panel still supports manual/interval refresh for full snapshots while the top bar of live counters updates instantly.

Storage Caching Layer

StorageService now includes an optional, very small TTL (default 5s) in‑memory cache to reduce repetitive chrome.storage.local.get calls for hot keys (rotationState, configs, flags). The cache auto‑invalidates on set / remove. TTL is intentionally conservative to avoid stale UI while still smoothing bursty access patterns from rotation + diagnostics requests.

If stricter consistency is required, the TTL can be set to 0 when constructing StorageService to disable caching.

About

Tabs Rotator is a Chrome extension designed to automate the process of rotating through multiple tabs at specified intervals.

Resources

Stars

1 star

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages