Chrome extension that streamlines filing pay-transparency-law complaints. Ships with New York Pay Transparency Law (§194-b) support today, filed with the NYS Department of Labor; architected as a per-state adapter registry so other states with similar laws (CA SB 1162, CO Equal Pay for Equal Work Act, WA SHB 1795, etc.) can be added by dropping in one new adapter file.
When you see a job posting on LinkedIn (or other supported boards) that’s missing a pay range, one click:
The extension never submits the complaint itself — it only prepares it for your review.
LinkedIn, Indeed, Glassdoor, ZipRecruiter, Monster, Greenhouse, Lever, Workday.
A generic fallback handles unknown sites by finding the largest <article> / <main> block.
git clone https://github.com/pandtlabs/equipay.git
cd equipay
npm install # fetches deps, syncs vendor/, builds icons, and produces dist/formfill.js
Then:
chrome://extensionsAfter npm install, the repo is loadable as-is (vendor/, icons/*.png, and dist/formfill.js are all produced by the postinstall hook).
| Command | What it does |
|---|---|
npm run build |
Runs all three build steps below in sequence (default after npm install) |
npm run sync-lib |
Refreshes vendor/jspdf.umd.min.js + vendor/html2canvas.min.js from node_modules/ |
npm run build-icons |
Rasterizes icons/icon.svg → icons/icon-{16,48,128}.png via sharp |
npm run build-formfill |
Bundles formfill/ (ES-module source) → dist/formfill.js via esbuild (IIFE, un-minified, Chrome 120 target) |
After editing files:
| If you changed… | Run… | Then… |
|---|---|---|
background.js, content.js, options.*, manifest.json, anything in vendor/ |
(nothing — not bundled) | chrome://extensions → equiPay → ↻, then reload any tab you want to test on |
Anything in formfill/ |
npm run build-formfill |
reload extension + reload the DOL tab |
icons/icon.svg |
npm run build-icons |
reload extension |
package.json deps |
npm install (triggers sync-lib) |
reload extension |
Content scripts stay resident in tabs until you reload those tabs — always refresh the LinkedIn or DOL tab after reloading the extension.
formfill/
├── index.js orchestrator (picks adapter by window.location.host)
├── lib/ shared DOM / input / file-upload / sanitizer / review-panel helpers
└── adapters/
├── index.js host → adapter registry
└── ny.js declarative config for NY DOL §194-b form
Adding a new state = one new file under adapters/, one entry in adapters/index.js, one host added to manifest.json’s host_permissions. See docs/ADDING_A_STATE.md for the step-by-step playbook, or docs/claude.md for full design notes.
For Chrome Web Store submission, produce a zip that contains the runtime files only — no formfill/ source, scripts/, docs/, node_modules/, or package*.json. The exact zip command + per-permission justifications + listing copy live in docs/STORE_LISTING.md.
Short version:
npm run build
zip -r equipay-0.1.0.zip \
manifest.json \
background.js content.js options.html options.js \
dist/formfill.js \
vendor/jspdf.umd.min.js vendor/html2canvas.min.js \
icons/icon-16.png icons/icon-48.png icons/icon-128.png
See docs/claude.md for the full design notes. TL;DR:
html2canvas on the job-description element directly (not the viewport), after temporarily neutralizing overflow / height on scroll ancestors so the full content is in the natural document flow.jsPDF directly, with a metadata header and paginated screenshot.formfill/adapters/ny.js is a pure declarative config of input names, label keywords, and review-panel copy. The orchestrator + shared helpers live in formfill/lib/. esbuild bundles it into dist/formfill.js. Adding a new state is one new adapter file.chrome.storage.local; nothing leaves your browser.MIT