Skip to content

Architecture

This page gives a high-level view of the code to help you orient before contributing. The detailed reference (conventions, Claude agents, E2E testing conventions) lives in the CLAUDE.md file of the repository.

FileRole
src/entrypoints/background.tsService worker (MV3) or background script (MV2). Delegates to src/background/.
src/entrypoints/content.content.tsContent script injected on every page. Captures middle-clicks and right-clicks on links.
src/entrypoints/popup.htmlHTML shell of the popup. Logic lives in src/popup.tsx.
src/entrypoints/options.htmlHTML shell of the Options page. Logic lives in src/options.tsx.

In src/background/:

ModuleRole
index.tsBackground bootstrap: registers listeners, wires the badge, initializes workspaces.
grouping.tsCreates Chrome groups from rules. Name extraction via regex on title / URL.
deduplication.tsDetects and closes duplicates. Handles undo via deduplicationSkip.
organize.tsThe Organize tabs action: applies rules to already-open tabs.
event-handlers.tsBinds tabs.onCreated, tabs.onUpdated, tabs.onRemoved listeners, etc.
messaging.tsBackground ↔ popup messages (group name prompt, notifications).
migration.tsOne-shot migrations: sync to local, addition of the fallbackLabel field, session archive.
settings.tsReads settings with a fallback to defaults.
actionBadge.tsG / D / X badge on the icon, tracks the global toggles of the active workspace.

In src/hooks/:

HookRole
useSettingsRead-write access to global settings. Uses refs to prevent races.
useStorageStateUnified facade over browser.storage.local and browser.storage.session.
useSessionsFacade for the three buckets (pinned, active, archived) with per-bucket hooks.
useSessionEditorSession editing: drag-drop, rename, tab deletion, reentrancy guard.
useStatisticsPer-workspace scoped counters.
useDeepLinkingSynchronizes the URL hash with the UI state (e.g. #sessions/archived).

In src/schemas/:

SchemaCovers
common.tsShared primitive types.
domainRule.tsA domain rule.
enums.tsEnumerations (naming modes, dedup strategies, restore actions, badges).
importExport.tsImport-export archive format, relaxed version to tolerate older files.
session.tsA session with its tabs and internal groups.
workspace.tsWorkspace metadata.
pack.tsPack manifest (packFileSchema).
category.tsA category (emoji, label, builtIn).

In src/components/:

  • Core/: components tied to a business concept. DomainRule/, Session/, Statistics/, TabTree/.
  • UI/: cross-cutting interface components. Header/, PopupHeader/, PopupToolbar/, Sidebar/, WizardStepper/, ImportExportWizards/, SessionWizards/, SettingsPage/, etc.
  • Form/: reusable form fields (FormFields/).

The split is documented in CLAUDE.md.

getMessage() in src/utils/i18n.ts is the single entry point for labels. Messages live in public/_locales/{en,fr,es}/messages.json. Every UI string (label, aria-label, title, placeholder) goes through getMessage(). No hardcoded strings.

Three pipelines:

  • Vitest (tests/): unit tests for utils, hooks, background modules.
  • Playwright E2E (tests/e2e/): functional journeys on Chrome MV3.
  • Playwright doc-scenarios (e2e-doc-scenarios/): narrative captures for the documentation. Not run on PRs.

The shared architecture across the three pipelines (Page Objects, Domain Actions, fixtures-base) lives in e2e-shared/ and is documented in its README.