History Graph #
A browser extension that shows your navigation history as a tree

Build and Package #
Requires Node 24 and npm
npm install # if you haven't already
npm run build # just build the extension
npm run package # build + zip → release/
Produces two loadable artifacts:
release/history-graph-chrome-<version>.zip— drag ontochrome://extensions, or just Load unpacked thedist/folder.release/history-graph-firefox-<version>.zip— built from a Firefox-flavored manifest written todist-firefox/(event-page background instead of a service worker, nativesidebar_actioninstead ofside_panel, plus a gecko add-on id).
To try it in Firefox: open about:debugging#/runtime/this-firefox → Load
Temporary Add-on → pick dist-firefox/manifest.json (temporary add-ons are
unsigned and clear on restart). The Chrome side panel becomes Firefox's native
sidebar — open it from the toolbar button. Requires Firefox 140+.
Develop #
npm install
npm run dev # vite build --watch → writes ./dist on every change
Then load it in Chrome:
- Visit
chrome://extensions, enable Developer mode. - Load unpacked → select the
dist/folder. - Browse around. Open the service-worker console (the service worker link
on the extension card) to watch
[history-graph] visit …logs. - Click the toolbar icon to open the side panel (it solos the active tab and updates live). Use the ⤢ button in the panel to pop out the full-tab view.
After code changes, npm run dev rebuilds automatically — just hit the reload
icon on the extension card to pick up the new build.
Test #
npm test # pure-logic checks (URL matching, forest assembly)
npm run test:e2e # builds, loads the extension in a real headed Chromium via
# Playwright, drives navigations, then reads the extension's
# own IndexedDB and asserts the captured tree (titles, edges,
# no same-URL dupes)
test:e2e (scripts/capture-check.mjs) is the real end-to-end check: it
launches Chromium with --load-extension=dist, so the actual service worker /
chrome.webNavigation capture path runs — not a mock.
How it works #
Capture (service worker) → IndexedDB → Query (getForest) → Viewer (GraphView)
webNavigation + tabs visits store {roots, children} indented tree (v1)
- Capture (
src/background): listens towebNavigation/tabs, builds aVisitper navigation, and reconstructs the parent edge from Chrome's transition metadata + which tab opened which. Ephemeral per-tab state lives inchrome.storage.sessionbecause the MV3 worker is killed between events. - Storage (
src/storage/db.ts): IndexedDB, indexed by time/tab/parent. - Query (
src/query/getForest.ts): the only thing renderers call. Turns stored visits into a normalized forest. - Viewer (
src/viewer): a React app.App.tsxowns view state and derives a view model (graph/viewModel.ts) from the forest; pure layout functions (graph/layout.ts) place nodes;views/GraphCanvas.tsxdraws lanes (SVG) + rows (HTML). Three views share that pipeline: Git graph (chronological, branches as vertical lanes), Tree (file-tree), and List. The layout/view-model code is framework-free and unit-tested.
The data model (src/model/types.ts) is flat and additive so it can grow
without migrations.
Status #
Work in progress. I'm pretty happy with it so far, but the history capturing heuristics need work wrt what counts as a separate entry.
Roadmap #
Loosely ordered, no promises I get to any of this.
Data model & capture fidelity #
UX & product #
Tooling & quality #
Visualizations #
I was originally going to play with other visualization techniques (and feel free to if you want to), but I think I'm quite happy with the git graph it currently uses.
Each is a layout strategy feeding the shared GraphCanvas: