Building a Chrome Extension for LinkedIn Job Import in Talior.me

I just pushed an update to talior.me that solves another annoying part of the job application workflow I kept running into myself.
A big part of using talior.me well is actually getting good job descriptions into the system quickly. That sounds simple, but in practice, it usually meant copying text out of LinkedIn, cleaning it up, switching tabs, pasting it into the app, and then repeating that process over and over for every role I wanted to tailor against.
That worked, but it never felt like a real workflow.
So this update was about making that step feel much more native.
The problem I wanted to solve
When I am applying seriously, speed matters. Not just speed in the resume-generation pipeline, but speed in getting from “this looks like a relevant role” to “I already have a tailored resume in progress.”
Before this PR, talior.me could already generate tailored resumes from job descriptions, but the input flow still had too much friction. The job was on LinkedIn. The tailoring pipeline was in talior.me. I was acting as the glue between them.
That meant I had to do too much manual work:
- open the LinkedIn posting
- copy the description
- switch back to talior.me
- create a new job
- paste the content
- wait for processing
- keep track of which LinkedIn posting mapped to which generated resume
That is fine a few times. It starts getting annoying when you are doing it repeatedly.
What I wanted instead was something much more direct: open a LinkedIn job posting, click one button, and let the rest of the system take over.
That felt like the missing piece.
If talior.me is supposed to support the actual application workflow, it should not only help with resume generation after I provide the input manually. It should reduce the effort of getting good job data into the system in the first place.
What’s new in this update
A Chrome extension that imports LinkedIn job data directly
The biggest piece in this PR is a dedicated Chrome extension that reads job data from LinkedIn and sends it straight into the talior.me pipeline.
When I am on a LinkedIn job page, the extension extracts the job title, company, location, full job description, and the LinkedIn job URL. Instead of making me manually move that data around, it can submit the posting directly to the API and start the tailoring flow from there.
That changes the feel of the product quite a bit.
Instead of LinkedIn being where I discover roles and talior.me being a separate tool I use afterward, the two now connect more naturally. The extension turns a live job posting into a resume-tailoring job with much less ceremony.
The extension is built around the messy reality of LinkedIn pages
One part I liked in this PR is that it does not assume LinkedIn has a single clean DOM structure.
The content script uses multiple selectors and fallbacks for job title, company, location, and description extraction. It also handles the fact that LinkedIn behaves like a SPA, where navigating between jobs often changes the content without a full page reload.
So this was not just a naive “query one selector and hope it works” implementation.
It is more defensive than that: the extension watches for URL changes, waits for the new job content to render, and then re-extracts the posting. It also normalizes LinkedIn job IDs and canonicalizes the job URL so the system can treat the same posting consistently, even if LinkedIn presents it through slightly different page shapes.
That matters because job-import tooling is only useful if it keeps working in the normal browsing flow, not just on one ideal page variant.
The pop-up now acts like a lightweight job control surface
This PR also adds a real pop-up flow instead of treating the extension like a dumb scraper.
The pop-up handles authentication, lets me tailor a resume from the current posting, shows progress while the backend is working, and gives me a one-click way to open the finished resume once processing is done.
I like that because it keeps the interaction tight.
I do not have to send the job blindly and then go hunt for the result somewhere else. The pop-up makes the extension feel like a small client for the talior.me job system rather than a one-off import button.
It also remembers jobs that were already submitted. So if I revisit the same LinkedIn posting later, the extension can map that LinkedIn job back to the existing talior.me resume job and show me the current state immediately instead of making me re-submit it.
That is a small UX detail, but it makes the whole thing feel much more coherent.
External job sources are now a first-class part of the data model
This PR is not just frontend convenience.
Under the hood, it also introduces an ExternalJobSource model tied to a resume job. That means talior.me can store provider-specific metadata like the external provider, the external job ID, the canonical URL, the raw URL, and any extra metadata without overloading the core resume job record.
That is the part I probably like most architecturally.
It would have been easy to treat LinkedIn import as just “stuff some URL into applicationUrl and move on.” But that would have made the system harder to extend later.
Instead, this PR treats external job platforms as a real concept in the product. Right now, the flow is centered on LinkedIn, but the model already points toward broader support for other sources too.
That gives the feature more room to grow.
Tracker and UI flows now understand where a job came from
Once external source data exists, the rest of the product can do smarter things with it.
This PR updates the tracker service and shared DTOs/types so resume jobs can carry external source metadata through the stack. On the app side, job views and tracker actions can now surface provider-aware links like “Open on LinkedIn” instead of showing a generic application URL.
That is a small touch, but it improves the product in a nice way.
A job in talior.me is no longer just an isolated resume-generation record. It now has a stronger connection back to the source posting that created it. That makes the workflow easier to understand when I am looking back at old applications.
Why this matters (for me)
This update came directly from how I actually use talior.me while job hunting.
When I am evaluating roles, there is a huge difference between:
“this looks interesting, let me manually move all this data into my tool”
and
“this looks interesting, let me click once and start tailoring”
That difference sounds small, but it changes the cadence of the workflow.
I want talior.me to be useful at the moment I discover a role, not only after I have already done a bunch of manual setup work. The Chrome extension pushes the product closer to that.
It also makes the system feel less like a resume editor and more like an application workflow tool.
The direction I care about is:
- discover a role
- import it directly
- generate a tailored resume
- preserve the source context
- reopen the posting later if needed
- keep everything tied together cleanly
That is much closer to how people actually apply.
What I like technically about this change
I like this PR because it is a good example of a product idea turning into a real cross-stack system instead of staying as a thin UI trick.
It touches:
- browser extension architecture
- DOM extraction and fallback handling
- SPA navigation detection
- auth and token refresh inside the extension
- local storage and migration logic
- backend job submission
- external source persistence
- shared DTO and type updates
- tracker-service integration
- provider-aware UI actions in the web app
Those are my favorite kinds of changes because they force the product concept to stay consistent all the way through implementation.
The extension is not useful unless the backend can accept the data cleanly. The backend integration is not enough unless the tracker can preserve source metadata. That metadata is less valuable unless the UI knows how to surface it. And none of it feels complete unless the flow is fast enough to actually use while browsing jobs.
That is what made this PR fun.