Skip to content

About

Chrome extension + web dashboard that tracks your LeetCode practice — time per problem, runs, submits, verdicts, topic coverage, friend comparison. MV3 · React · Supabase.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

40 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

LeetCode Tracker

Know how much time you actually spend on LeetCode, which topics you neglect, and which problems you keep coming back to.

A Chrome/Brave extension that quietly tracks your LeetCode practice — time spent, runs, submits, verdicts — and a web dashboard that turns it into something useful: solved-by-difficulty badges, a topic-time chart, a filterable problem list, and friend comparison.

→ Try the dashboard · → Install the extension (invite-only) · → Read the technical writeup

Dashboard overview


Why this exists

LeetCode shows you what you've solved. It doesn't show:

  • Where your time actually goes — that 30 min on Two Sum vs 3 hours stuck on Trapping Rain Water
  • Which topics you avoid — you've done 18 array problems and 0 graph problems this month
  • What your real attempt history looks like — every run, every submit, every verdict, timestamped
  • How you compare to friends grinding the same problems

This fills those gaps with one Chrome extension and one dashboard. No screenshots, no manual logging, no behavior change on your part — it just runs in the background while you practice as normal.


How it works

┌─────────────────────┐         ┌─────────────────────┐         ┌─────────────────────┐
│  Browser Extension  │         │     Supabase        │         │   Web Dashboard     │
│                     │  sync   │                     │  read   │                     │
│  • Time tracking    │ ──────► │  • Postgres + RLS   │ ──────► │  • Stats + charts   │
│  • Run/Submit obs.  │  every  │  • Auth (invite)    │  live   │  • Problem list     │
│  • Verdict watcher  │  60s +  │  • Single-use codes │         │  • Friends compare  │
│  • Local catalogue  │  events │                     │         │                     │
└─────────────────────┘         └─────────────────────┘         └─────────────────────┘
       MV3 + JS                    Postgres + GoTrue              React + Vite + Recharts

                        ✦  Zero requests to leetcode.com  ✦
                  (all observation is passive via chrome.webRequest)

A few design choices worth calling out:

  • Zero LeetCode network traffic. The extension makes no requests to leetcode.com at all — Run/Submit detection is passive (it watches the user's own POSTs via chrome.webRequest), and problem metadata is served from a catalogue bundled inside the extension (3,943 problems, ~114 KB gzipped, scraped once by the maintainer). From LeetCode's perspective, a user with the extension installed is indistinguishable from one without it. No basis for an account flag.
  • chrome.webRequest for Run/Submit detection (not page-level fetch hooks). LeetCode caches a native fetch reference before any content script can patch it, so MAIN-world overrides silently miss every request. webRequest sits at the network layer and catches everything.
  • Bundled problem catalogue. Difficulty, topics, and premium-status for every public problem are shipped inside the extension (extension/data/problems.json). The local lookup replaces what used to be a runtime GraphQL call. Refresh via node scripts/scrape-problems.js and re-release.
  • Time tracking treats "opened-and-bounced" as noise. A visit without any run, submit, or 30+ seconds of input is discarded at the sync boundary. Your dashboard reflects real practice, not curiosity.
  • Phantom-time safeguard. Tab closed without firing pagehide? The background worker caps the active interval after 90 seconds of silence so the timer doesn't accumulate hours of imaginary time.
  • Row Level Security on every table. Anon key is shipped in the client; access control is enforced at the database layer, not the application layer.
  • Single-use invite codes gate signups. Codes live in a table the anon key cannot read, validated by a SECURITY DEFINER trigger on auth.users insert.

Tech

Layer Stack
Extension Manifest V3 · vanilla JS · ES modules · chrome.webRequest
Dashboard React 19 · Vite 8 · Recharts · date-fns
Backend Supabase (Postgres 15 · GoTrue · PostgREST · pg_cron)
Hosting Netlify (dashboard) · Supabase (DB + auth) · GitHub Releases (extension)
Distribution Unpacked .zip (current) → Chrome Web Store (planned)

Total footprint: ~3000 lines across extension + dashboard + SQL migrations.


Install (invite-only)

Signups require a single-use code. DM me if you'd like one.

  1. Download the latest leetcode-tracker-X.Y.Z.zip from the releases page and unzip it.
  2. Open chrome://extensions (or brave://extensions) → toggle Developer mode ON.
  3. Load unpacked → pick the unzipped folder.
  4. Open the dashboard and Sign up with your email, a password, and the invite code you were given.
  5. Click the extension icon → Sign in with the same credentials.
  6. Open any leetcode.com/problems/ page. Practice as usual. Your dashboard updates within ~60 seconds of each Run or Submit.

Repo layout

extension/          Manifest V3 Chrome extension
  ├ background.js     service worker (sync, webRequest, attempt lifecycle, catalogue lookup)
  ├ content.js        isolated-world script (slug detection, idle tracking)
  ├ page-hook.js      MAIN-world script (verdict-watch backstop)
  ├ supabase.js       thin REST/auth wrapper, no SDK
  ├ popup.html/.js    extension popup UI
  ├ config.js         public Supabase URL + anon key (safe to commit, RLS enforces)
  ├ data/
  │   └ problems.json bundled metadata for ~3,943 problems (zero runtime LeetCode calls)
  └ build-zip.sh      packages the release zip

dashboard/          React + Vite SPA
  └ src/
      ├ pages/        Dashboard + Auth + Friends
      ├ components/   StatsBar, ActivityChart, Heatmap, TopicChart, ProblemList, DifficultyBadges
      ├ hooks/        useAttempts, useProblems, useFriendships, useSession
      └ utils/stats.js  pure aggregation (testable, no React, no Supabase)

supabase/           SQL migrations (idempotent, re-runnable)
  ├ schema.sql              core tables + RLS + triggers
  ├ phase4.sql              friends/groups
  ├ phase4_usernames.sql    username uniqueness
  ├ phase5_event_log.sql    granular event tracking + 90-day cron cleanup
  ├ phase6_invite_codes.sql single-use signup codes
  └ phase7_lockdown_problems.sql  immutable shared catalogue

scripts/            admin tooling (runs only on owner machine)
  ├ new-invite.sh           generate a single-use invite code
  ├ scrape-problems.js      regenerate extension/data/problems.json from LeetCode
  └ .env.example            service_role config (real .env is gitignored)

Local development

For forking or self-hosting your own instance:

Click to expand

Supabase

  1. Create a project at supabase.com (free tier is plenty).
  2. SQL Editor → run each file in supabase/ in numeric order (schema.sql first, then phase4.sql, etc).
  3. Project Settings → API → copy the Project URL and anon public key.

Extension

  1. Edit extension/config.js — replace the URL and anon key with your own project's values.
  2. chrome://extensions → Developer Mode → Load unpacked → pick the extension/ folder.

Dashboard

cd dashboard
cp .env.example .env
# Set VITE_SUPABASE_URL and VITE_SUPABASE_ANON_KEY
npm install
npm run dev    # → http://localhost:5173

Deploy dashboard/ to Netlify, Vercel, or any static host. Build command: npm run build, output dir: dist.

Generate invite codes (admin)

cp scripts/.env.example scripts/.env
# Fill in SUPABASE_URL and SUPABASE_SERVICE_ROLE_KEY (the latter NEVER leaves your machine)
./scripts/new-invite.sh "Note for this invite"

Refresh the bundled problem catalogue (admin)

Re-scrape LeetCode's public problem list, write a fresh extension/data/problems.json, then ship a new extension version:

node scripts/scrape-problems.js
# bumps to ~3,943 problems in ~60 seconds (polite, 1.5s/page, unauthenticated)

# Then bump version in extension/manifest.json, rebuild, release:
./extension/build-zip.sh
gh release create vX.Y.Z extension/leetcode-tracker-X.Y.Z.zip --title "..." --notes "..."

Monthly is plenty — LeetCode adds maybe 5-10 problems per week.


Security model

A few notes since this is a multi-user system with a public-anon client:

  • Anon key is committed. It's safe by design — Supabase anon keys are intended for client use; access control happens at the database via Row Level Security policies, not by hiding the key.
  • service_role key is admin-only. Lives only in scripts/.env on the owner's machine. Never shipped, never committed.
  • Every table has RLS enabled. Users can read/write only their own rows except problems (a shared catalogue with read-anyone / insert-only-from-clients / update-admin-only).
  • No client-initiated LeetCode requests. The extension's only outgoing traffic goes to your own Supabase project (for sync). LeetCode is observed passively via chrome.webRequest. No theoretical basis for a LeetCode account ban from extension behaviour.
  • Invite codes use a SECURITY DEFINER trigger that locks the row (SELECT FOR UPDATE) to prevent race conditions when two signups attempt the same code.
  • Events are auto-purged. pg_cron runs nightly to delete events older than 90 days, keeping storage bounded at the free-tier limit.

License

MIT — see LICENSE.


Built by @iprashantraj. Open an issue for bugs or feature requests — there's also a "Report a bug" link inside the extension popup.

About

Chrome extension + web dashboard that tracks your LeetCode practice — time per problem, runs, submits, verdicts, topic coverage, friend comparison. MV3 · React · Supabase.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages