Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

8 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

FormFill AI

A Chrome extension that suggests answers for job application form fields — never auto-fills, always shows a suggestion first so you stay in control.

What it does

When you focus a form field on a supported job site, a tooltip appears below the field with a suggested value pulled from your profile or generated by an LLM. You decide whether to accept it, dismiss it, or regenerate with feedback.

Two suggestion modes:

  • Simple fields (name, email, phone, location, LinkedIn, GitHub, etc.) — resolved instantly from your saved profile using deterministic lookup with fuzzy matching. No network call.
  • Complex fields (cover letters, "why this company", salary, visa/sponsorship, open-ended questions) — sent to an LLM that reads your full profile plus the job description extracted from the current page. The suggestion includes a collapsible "Why this answer?" reasoning section.

Regeneration with feedback — if the suggestion isn't right, click Regenerate, type optional context ("prefer $130k+", "mention my Python background", "make it shorter"), and the LLM incorporates your feedback in the next attempt.

Supported sites: Greenhouse, Workday, Lever, LinkedIn, Indeed, SmartRecruiters, Jobvite, Taleo, iCIMS, BambooHR, Ashby, Rippling, ApplyToJob.


System architecture

┌─────────────────────────────────────────────────────┐
│                  Chrome Extension                   │
│                                                     │
│  ┌──────────┐   ┌──────────────────────────────┐   │
│  │  Popup   │   │       Content Script          │   │
│  │(React/TS)│   │  detector → classifier →      │   │
│  │          │   │  suggester → tooltip UI       │   │
│  │ Auth +   │   │  (Shadow DOM, per-field)      │   │
│  │ Profile  │   └──────────────┬───────────────┘   │
│  └────┬─────┘                  │ chrome.runtime     │
│       │ chrome.storage.local   │ .sendMessage       │
│       │ (JWT + profile)        │                    │
│  ┌────┴────────────────────────▼───────────────┐   │
│  │           Service Worker (background)        │   │
│  │  - reads JWT from storage                   │   │
│  │  - session cache (SHA-256 key per field)     │   │
│  │  - proxies to Supabase Edge Function         │   │
│  └────────────────────────┬────────────────────┘   │
└───────────────────────────│─────────────────────────┘
                            │ HTTPS (Bearer JWT)
          ┌─────────────────▼──────────────────┐
          │        Supabase (cloud)             │
          │                                     │
          │  Auth  ──►  profiles table (RLS)   │
          │                  │                  │
          │         Edge Function: /suggest     │
          │         - verifies JWT              │
          │         - fetches profile from DB   │
          │         - calls Groq LLM            │
          │         - returns { reasoning,      │
          │                     suggestion }    │
          └─────────────────────────────────────┘

Major components

Extension — extension/src/

File Role
content/detector.ts Queries all visible input, textarea, select elements; extracts a human-readable label via a 5-step chain (aria-label → associated <label> → placeholder → name → id)
content/classifier.ts Decides whether a field is simple (deterministic lookup), complex (LLM), or skip (password, hidden, etc.) based on label keywords
content/field-map.ts ~60-entry lookup table mapping normalized label strings to dot-paths in ProfileData; includes Levenshtein fuzzy matching (distance ≤ 2) for typos and variations
content/jd-extractor.ts Extracts the job description from the current page using site-specific selectors (Workday data-automation-id, Greenhouse #content, etc.), capped at 3000 chars
content/suggester.ts Orchestrates the full suggestion flow: loads profile, calls classifier, resolves simple fields locally or requests LLM suggestions via the service worker, manages tooltip lifecycle
content/ui/tooltip.ts Self-contained Shadow DOM tooltip (style isolation). States: loading spinner → suggested answer + accept/dismiss/regenerate buttons → optional context input for feedback-guided regeneration → collapsible reasoning panel
background/service-worker.ts Chrome MV3 service worker. Reads JWT directly from chrome.storage.local (no Supabase import — avoids WebSocket crash). Maintains a chrome.storage.session cache keyed by SHA-256 of fieldLabel + pathname. Proxies SUGGEST_FIELD messages to the Edge Function
popup/ React app for sign-in (Supabase email/password auth) and profile editing. Writes profile to both Supabase Postgres and chrome.storage.local so the content script can read it synchronously
shared/types.ts ProfileData type: personal, education[], experience[], projects[], skills[]

Supabase — supabase/

Component Role
migrations/001_profiles.sql profiles table with JSONB columns for each profile section. Row-level security ensures users can only read/write their own row. A Postgres trigger auto-creates an empty profile row on signup
functions/suggest/index.ts Deno Edge Function. Verifies the JWT, fetches the caller's profile, builds a structured prompt (profile + job description + field label + optional user feedback), calls Groq llama-3.3-70b-versatile, parses the JSON response, returns { reasoning, suggestion, source }

Data flow — complex field suggestion

User focuses field
       │
       ▼
detector.ts finds element + label
       │
       ▼
classifier.ts → "complex"
       │
       ▼
suggester.ts shows loading tooltip
       │
       ▼ chrome.runtime.sendMessage("SUGGEST_FIELD", { label, jobDescription, pathname, userContext })
       │
       ▼
service-worker.ts
  ├─ cache hit? → return cached { reasoning, suggestion }
  └─ cache miss → fetch Edge Function with JWT
       │
       ▼
Edge Function
  ├─ verify JWT
  ├─ load profile from DB
  ├─ buildPrompt(label, jobDescription, profile, userContext)
  └─ Groq LLM → parse JSON → return { reasoning, suggestion }
       │
       ▼
service-worker caches result, sendResponse back
       │
       ▼
tooltip.ts resolves: shows suggestion + reasoning toggle
       │
       ▼
User: Accept → applyValue() (native setter + input/change events for React/SPA compat)
       └── Regenerate → context input → re-runs with userContext injected into prompt

Local setup

# 1. Install extension dependencies
cd extension
npm install

# 2. Copy env file and add your Supabase project URL
cp .env.example .env
# VITE_SUPABASE_URL=https://<ref>.supabase.co
# VITE_SUPABASE_ANON_KEY=<anon key>

# 3. Build
npm run build
# Output: extension/dist/

# 4. Load in Chrome
# chrome://extensions → Developer mode → Load unpacked → select extension/dist/

# 5. Deploy the Edge Function
npx supabase functions deploy suggest
# Set GROQ_API_KEY in Supabase dashboard → Project Settings → Edge Functions

Key design decisions

No auto-fill — the extension only suggests. The user explicitly clicks "Use this". This avoids overwriting fields the user has already typed into and keeps the experience trustworthy.

Profile stays local — profile data is written to chrome.storage.local by the popup, so simple-field lookups are instant and work offline. The Edge Function reads from Postgres only for LLM calls (where network latency is already unavoidable).

Service worker has no Supabase import — importing the Supabase JS client into a service worker causes a crash because Supabase Realtime tries to open a WebSocket, which MV3 service workers don't support. The JWT is read directly from storage instead.

Shadow DOM tooltip — style isolation prevents the host page's CSS from affecting the tooltip and vice versa. The host element uses position: fixed; z-index: 2147483647 to stay above page content.

Pathname-scoped cache — suggestions are cached per SHA-256(fieldLabel + pathname) for the browser session. Using pathname (not hostname) means different job postings on the same site get fresh LLM suggestions.

About

Help fill job applications

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages