A Firefox extension that replaces the new tab page with a grid of container-organized links — your bookmarks, sorted by the containers you already use.
- One-click container tabs — click any link to open it in the right container, no extra steps
- Right-click any tab to add it to your home page instantly
- Drag and drop to reorder containers and links however you like
- Firefox Sync — your home page follows you across devices automatically
- Site icons — optionally fetch favicons via Icon Horse, off by default for privacy
- Light, dark, or auto theme with optional per-device override
- Minimal permissions — no tracking, no analytics, no access to your browsing
- Node.js
- Firefox 140+
npm installnpm run buildBundles TypeScript sources into dist/ (worker + page + static assets).
npm startOr for live rebuild + run:
npm run devnpm run lintnpm run packageProduces a .zip in artifacts/.
npm testRuns Vitest against the pure model (mutations, reconciliation, quota, write planner, orchestrator). No browser needed — all logic is tested with in-memory mock storage.
npm run test:watchRuns Vitest in watch mode for development.
Browser tests use Playwright to run the built extension in Firefox.
npm run build
npm run test:browserNote: Browser tests require PLAYWRIGHT_FIREFOX_EXT=1 to actually run (they skip by default). Firefox extension loading in Playwright uses internal APIs that may not be available in all environments.
To test against an existing Firefox profile (e.g., one with containers already configured, or with Firefox Sync enabled):
FIREFOX_PROFILE_PATH=/path/to/profile npm run test:browserFinding your profile path:
- Open Firefox
- Navigate to
about:profiles - Copy the "Root Directory" path for the profile you want
Creating a dedicated test profile:
firefox -CreateProfile "extension-test"Then find its path in about:profiles or under:
- Linux:
~/.mozilla/firefox/ - macOS:
~/Library/Application Support/Firefox/Profiles/
Using a profile for manual testing:
npm run build
web-ext run --source-dir=dist --firefox=firefox --firefox-profile=/path/to/profile --keep-profile-changesThe --keep-profile-changes flag preserves storage data between runs — useful for testing sync scenarios or migration from existing data.
Without a profile: Playwright creates a temporary profile with containers enabled via Firefox preferences. Tests create their own containers via the contextualIdentities API. The temp profile is discarded after the test run.
The extension uses a Manifest V3 service worker as the sole authority for state mutation and sync reconciliation. The new tab page is a thin view that reads storage.local and sends mutations to the worker via runtime.sendMessage.
- Worker writes
storage.localandstorage.sync - Page reads
storage.localdirectly and builds DOM - Sync reconciliation uses presence-wins union with tombstones for deletes
- Dirty flags ensure pending sync writes survive worker termination
- No conditional sync/no-sync logic — same code paths regardless of Firefox Sync status
| Permission | Why |
|---|---|
contextualIdentities |
Query Firefox containers |
cookies |
Required by the contextualIdentities API |
tabs |
Open links in container tabs |
menus |
"Add to home page" tab context menu |
storage |
Persist link/container configuration, Firefox Sync |
MIT