LinkedIn Enhancer
by José DA COSTA
Des outils de productivité qui améliorent LinkedIn.
LinkedIn Enhancer ajoute des outils concrets directement dans LinkedIn : il empêche le fil de se recharger quand vous changez d'onglet, stoppe la lecture automatique des vidéos, masque les pubs et le superflu du fil (posts sponsorisés, suggestions, recommandations, changements de poste, module LinkedIn Actualités), copie n'importe quel post et tout son fil de commentaires sous forme de prompt prêt pour l'IA, peut archiver tout ce que vous faites défiler dans une base locale sur votre appareil, ajoute un bouton « Pas intéressé » en un clic. Chaque outil est un simple interrupteur, rien n'est envoyé en ligne, et tous les réglages restent dans votre navigateur.
- Gardez votre position de lecture et votre brouillon en changeant d'onglet
- Stoppez la lecture automatique des vidéos du fil au défilement
- Un clic transforme un post, son lien et ses commentaires en prompt IA
- Archivez tout ce que vous faites défiler dans une base locale et privée
- Masquez pubs, suggestions, recommandations, changements de poste, promos emploi & Learning et Actualités
- Un bouton « Pas intéressé » pour affiner votre fil en privé, en un clic
- Aucune donnée ne quitte votre navigateur ; chaque outil s'active seul
Disponible pour
Les builds s'installent en mode développeur de votre navigateur.
Pourquoi c'est utile
LinkedIn recharge votre fil dès que vous changez d'onglet, ce qui efface votre position de lecture et le commentaire que vous étiez en train d'écrire. Les vidéos démarrent toutes seules, tandis que les posts sponsorisés, les incitations Premium, les posts suggérés, les cartes « personnes à suivre », les changements de poste et le module Actualités repoussent les vraies actualités hors de vue. Et quand un post lance une bonne discussion, il n'existe aucun moyen propre d'en extraire le texte pour l'analyser. Cette extension supprime ces frictions sur place — en privé, tout restant sur votre appareil.
Fonctionnalités
Stopper le rafraîchissement automatique du fil
Conserve votre position de défilement et votre brouillon de commentaire quand vous changez d'onglet puis revenez.
Stopper la lecture automatique des vidéos
Les vidéos du fil ne démarrent plus seules au défilement ; elles ne se lancent qu'au clic.
Copier un post en prompt prêt pour un LLM
Un clic copie un post, son vrai lien et tout son fil de commentaires, formaté pour un LLM. Le texte du prompt est entièrement personnalisable.
Archiver les posts que vous faites défiler
Enregistre, en option, chaque post vu dans une base locale sur votre appareil, que vous pouvez prévisualiser et exporter en JSON, CSV ou XLS. Rien ne quitte votre navigateur.
Masquer les pubs et posts sponsorisés
Remplace le contenu sponsorisé et promu, ainsi que les incitations Premium de LinkedIn, par un petit espace réservé.
Masquer les posts suggérés
Remplace les cartes « Suggestions » du fil par un espace réservé (désactivé par défaut).
Masquer les recommandations de profil
Supprime les cartes « Recommandé pour vous » (personnes à suivre) du fil.
Masquer les changements de poste
Supprime du fil les cartes d'occasion « a commencé un nouveau poste ».
Masquer LinkedIn Actualités
Supprime le module « LinkedIn Actualités » de la colonne de droite et ses actualités à la une.
« Pas intéressé » en un clic
Ajoute un bouton « Pas intéressé » à côté de « J'aime » qui indique en privé à LinkedIn d'afficher moins de contenu de ce type — personne n'est averti.
Notifications de mise à jour
Vous prévient quand une nouvelle version est publiée pour la télécharger et l'installer.
Permissions
Historique des versions
Fixed
- "Hide LinkedIn Learning promos" now catches every card. Some LinkedIn Learning promo headers render the brand with a non-breaking space ("LinkedIn Learning"), which slipped past the regular-space matcher and left those cards visible. The feed-clutter scan now normalizes non-breaking spaces before matching, so all variants of the promo are hidden as intended.
Removed
- The "Show relationship status" tool is gone. The small per-post badge that
showed the author's connection degree (1st/2nd/3rd) and whether you follow them
— and its
showRelationshipStatussetting, the popup/options toggles, therelationship-statusmodule and its i18n strings — have all been removed.
Added
- Hide LinkedIn Learning course promos. A new opt-out toggle (on by default) replaces the in-feed LinkedIn Learning promo cards ("Cours populaire sur LinkedIn Learning" / "Popular course on LinkedIn Learning") with a small placeholder — same mechanism as the other clutter hiders. Matched by a short feed header that carries the "LinkedIn Learning" brand (the bare logo label is ignored), so it works across the various course-card titles and locales.
Added
- Hide LinkedIn's in-feed job-search promos. A new opt-out toggle replaces the "Trouvez des postes à pourvoir" / "Find jobs for you" promo cards LinkedIn injects into the feed with a small placeholder — same mechanism as the other clutter hiders (multilingual, reversible, counted toward the toolbar badge).
Added
- The toolbar icon shows the extension is inactive off LinkedIn. On any non-LinkedIn tab the icon is now grayed out (faded), and clicking it shows a short reassuring notice — "You need to be on LinkedIn — this extension only works on LinkedIn…", with an "Open LinkedIn" button — instead of the settings panel. On LinkedIn the icon is colored and the popup shows the tools as before. This makes it obvious the extension does nothing on other sites. (Graying degrades gracefully where the canvas API is unavailable; the popup notice always shows.)
Fixed
- Brave is now listed and downloadable on the showcase page. Brave is
Chromium and runs the Chrome build verbatim, but the public listing showed it
as unsupported and offered no Brave download. The release now also publishes a
Brave-labeled zip (a copy of the Chrome build, via
pnpm zip:brave) and the showcase lists Brave among the supported browsers (Chrome, Firefox, Edge, Brave, Opera).
Changed
- "Not interested" tooltip now warns about irreversibility and appears instantly. The button's tooltip shows immediately on hover/focus (a custom instant tooltip instead of the delayed native one) and now leads with the warning that the click cannot be undone and durably changes LinkedIn's algorithm — so it should only be used on content you are 100% sure you do not want.
Changed
- Update notifications are gentler and dev-mode aware. The daily "new version
available" check now runs only on manual installs (store-managed installs
auto-update, so they are skipped via
management.getSelf()— no extra permission), fetches the version at most once a day no matter how often the MV3 service worker restarts, and reminds at most once a day. Once you click the notification (acknowledging it), it never nags again for that version — only a newer release re-prompts.
Changed
- The showcase page now updates automatically on every release. This repo is
the source of truth for the public listing content (
store/extension.json, in English/French/Portuguese); the release workflow regeneratesextension.json— with the permissions derived from the built manifest — and pushes it to the releases repo, which revalidates the site. The listing's features, permissions and descriptions can no longer drift from what ships (the previous page still showed the removedsidePanelpermission and a stale feature list).
Removed
- The side panel is gone. It only duplicated the toggles already in the popup
and the options page, on the same storage — no feature of its own. Removing it
drops the
sidePanelpermission and theside_panelmanifest entry, deletes thesrc/entrypoints/sidepanel/surface, and leaves two consistent UI surfaces (popup + options) across every browser.
Changed
- Clearer "Not interested" tooltip. It now says the action is completely private and that no one (not just the author) is notified — contrasted with a Like, which appears on your profile.
Added
- "Not interested" button in the post action bar. A new opt-out button sits next to "Like" and, on click, drives LinkedIn's own "..." menu → "Not interested" — same effect as doing it by hand (the post leaves your feed and future recommendations are tuned away from this topic/author; private, the author is not notified). It reuses LinkedIn's thumbs-down icon and, unlike a public Like, can be used freely and repeatedly. All the menu-driving happens only on click — injecting the button does no identifier lookup, so it does not slow the feed. A hover tooltip summarizes what the feature does.
Added
- Hide LinkedIn News. A new opt-out toggle removes LinkedIn's "News" modules (the right-rail "LinkedIn Actualités" card with trending stories). The module is identified with certainty by its exact localized heading plus a card holding several story links, then hidden in place; the climb stops before the rail footer so the About/copyright links are never removed.
Changed
- Suggested posts are no longer hidden by default. The "hide suggested posts" toggle now starts OFF (existing users keep their current setting).
Fixed
- The copy prompt's default URL header now carries the
{{url}}variable. The resolved post link lands inside that line via the variable instead of being appended separately — no duplicate when the header has{{url}}, the raw URL is still appended for a custom header without it, and a{{url}}placed elsewhere skips the header. Reset the prompt to pick up the new default.
Fixed
- Comment scraping retries LinkedIn's "couldn't load comments" error. When the thread comes back as "Impossible de charger les commentaires" (and its localized variants), the Copy button and the comment archive now click "Try again" — up to a few times — instead of capturing an empty thread.
- Line breaks in a post's text are now preserved everywhere. Extraction keeps
the real
\ncharacters LinkedIn uses and also converts<br>elements to newlines, so the copied prompt and the archived record match the post's paragraph structure. The archive preview renders that structure too (it was collapsing every line break onto one line).
Added
- Hide "job change" posts. A new opt-out toggle replaces LinkedIn's job-change occasion cards ("Changement de poste de <name>" / "<name> started a new position") with a small placeholder, the same way the sponsored/suggestion/ recommendation hiders work. Multilingual, reversible, and counted toward the toolbar badge.
{{url}}variable for the copy prompt. When "Include post URL" is on, the resolved post link is now available as a{{url}}placeholder you can drop into any prompt field (intro, instructions, headers) — the options show the token in grey next to that checkbox. When you place{{url}}yourself, the link is no longer also appended at the end (no duplicate); leave it out and the trailing URL line behaves exactly as before.
Fixed
- Post archive captured the wrong author, text and comment count on some posts. Verified live on the feed, three extraction bugs surfaced and are fixed: (1) the author was taken from the social-proof header ("X aime ce contenu") whose profile link precedes the real author's — it now reads the real author from the post's control/hide button aria-label; (2) the caption occasionally captured LinkedIn's screen-reader "Feed post" heading instead of the real text, now stripped; (3) the comment/reaction/repost counters could capture a blank from a number-less label, now requiring an actual digit. The author's headline also no longer includes the mashed name + connection-degree prefix. Covered by new regression tests.
Added
- Post archive — save everything you scroll past. A new opt-out tool records
every post that enters the viewport into a local
chrome.storage.localdatabase that never leaves the device, building over time a record of everything seen in the feed. Each capture keeps all the useful information the render exposes (or hides): author and their headline/title, the post text, reaction count and the reaction types present, comment and repost counts, and how long ago it was posted. Posts are classified as classic, suggestion or sponsored, and three checkboxes pick which types to archive (the suggestion/sponsored boxes are greyed out when the matching "hide" tool is on, since hidden posts are never rendered to capture). Each post is deduped by a content hash so scrolling up and down stores it once. The options page shows the live count, an inline preview of the stored rows, and lets you reset the database or export it as JSON, CSV or XLS. Two heavy opt-in options (default off) additionally capture the real post link (via the "…" menu, like the Copy button) and the full comment thread (by expanding it, like the Copy button). The popup also exposes the archive toggle. The options and update sections spell out that the archive is local-only and which repository URL the update check fetches. Adds theunlimitedStoragepermission so the archive can grow large. - Copy-button prompt is fully localized, with injectable variables. The
prompt strings the Copy button assembles are now translated end to end —
French when the UI is French, English when it is English — instead of being
hard-coded English with a single inline language word. They also support
variable placeholders (
{{language}},{{locale}},{{date}},{{datetime}}) that are substituted at copy time, so the same template adapts to the active display language and the current date.
Changed
- The Copy button now copies the REAL post link. Modern LinkedIn exposes the
post's permalink nowhere in the DOM (no URN attribute, no anchor), so the copy
prompt used to fall back to the bogus
https://www.linkedin.com/feed/page URL. It now drives LinkedIn's own flow — opens the post's "…" menu and clicks "Copy link to post" (multilingual), which makes LinkedIn write the canonical URL to the clipboard, then reads it back. When no real link can be resolved, the URL line is omitted entirely (never the/feed/placeholder). Adds theclipboardReadpermission for the read-back.
Fixed
- The Copy button's icon now always shows (and matches LinkedIn's weight).
The icon was built by cloning the native Comment button and only set
if (svg !== null); when LinkedIn had not lazily rendered that icon yet at clone time, the copy button captured an empty placeholder and stayed icon-less (it appeared only "sometimes"). We now build our own<svg>deterministically, replace whatever sits in the icon slot, and repair any icon-less button on the next pass. The glyph is LinkedIn's own soliddocument-copyicon (extracted from their icon bundle intojose/icons/), so it no longer looks thin next to the native Like/Comment/Repost/Send icons.
Added
- Stop videos from autoplaying. A new opt-out toggle blocks LinkedIn's feed
videos from starting on their own. LinkedIn auto-plays the video nearest the
viewport through an
IntersectionObservercalling Video.jsplayer.play()(there is no hover handler — the "it starts when I mouse over it" impression comes from scrolling it into view). A MAIN-world content script (linkedin-stop-video-autoplay) overridesHTMLMediaElement.prototype.playto refuse any start that is not preceded by a genuine user gesture (event.isTrusted), so a real click still plays the video while the automatic start on scroll is blocked. Full analysis injose/blog/2026-05-30-linkedin-bloquer-lecture-automatique-videos-feed.md.
Fixed
- Graceful handling of an invalidated extension context. When the extension
is reloaded or updated (a dev-mode HMR rebuild, or a production auto-update)
while a LinkedIn tab stays open, the already-injected content script kept
running with a severed
browser.runtimehandle — turning into a "zombie" that spammed thousands ofchrome-extension://invalid/requests while the tools (Copy button, clutter hiding, counters) silently stopped working. The content script now detects the revoked context (via@lib/context-guard), tears down its DOM watcher, and shows a one-click "Reload" notice instead of lingering broken until a manual reload. It also swallows the one-shot "Extension context invalidated" error (and its async rejection) in its own world, and bails out quietly if it is (re)injected onto an already-dead context, so the transition no longer surfaces as an uncaught content-script error.
Changed
- Feed-clutter hiding now does one combined DOM scan instead of three.
applyFeedClutterused to run the label-matching loop once per category (sponsored, suggestions, recommended), re-scanning the whole document and re-allocating every element'stextContentthree times on every watcher tick. It now scansspan, p, h2, diva single time and tests the three matchers together (behavior identical — a label matches at most one category), roughly halving the per-tick CPU and GC pressure on large feeds. - Documented the feed's migration to React Server Components. The desktop
feed now flows through
flagship-web/rsc-action/ SDUI ids (e.g.com.linkedin.sdui.pagers.feed.mainFeed), and the refresh is the SDUI actionproto.sdui.actions.core.RefreshScreen, not the legacy VoyagervoyagerFeedDashMainFeedquery. The stop-feed-refresh module doc was rewritten to reflect this: the version-agnostic Page Visibility override is the primary defense (verified effective), the Voyager fetch-block is kept only as a legacy fallback, and the rare server-pushedRefreshScreenis documented as a known, not-yet-intercepted residual. - Simplified the extension name and description. The title shown in
chrome://extensionsis now just "LinkedIn Enhancer" (dropped the "by José DA COSTA" suffix), and the description drops the "Built with WXT, React, and TypeScript" tail, in both the en and fr locales.
Added
- Per-category hidden counters in the popup and side panel. Each of the four "hide" toggles (sponsored posts, LinkedIn ads, post suggestions, profile recommendations) now shows a count pill of how many items it has removed on the active tab, breaking down the single total already painted on the toolbar icon. The counts are cumulative: because LinkedIn virtualizes the feed (off-screen posts are dropped from the DOM), each item is counted once the moment it is hidden and the running total keeps climbing as you scroll, rather than shrinking. Toggling a category off restores its items and resets its counter. A separator was also added before the relationship-status toggle for visual grouping.
Security
- Build leak gate (
pnpm security:build). A dependency-free script (scripts/check-build-security.mjs) scans every production build dir and fails if it ships a source map (.map, externalsourceMappingURL, or inline map /sourcesContent), an original source file, dev-server / HMR code (localhost:3000,vite-hmr, …) or an absolute dev path. It runs inpnpm validateand as a hard stop in the release workflow before anything is published, so a release can never leak source maps or code.
Removed
- Dropped Safari support. Packaging a Safari Web Extension requires Xcode,
and the MV3 migration it would need (for the stop-feed-refresh
world: 'MAIN'content script) leaked thesidePanelconfig through a WXT browser-detection quirk. All Safari targets, scripts, permissions and docs were removed. The extension now targets Chrome, Edge, Opera, Brave and Firefox.
Changed
- Firefox is now built as Manifest V3. The stop-feed-refresh tool needs a
world: 'MAIN'content script to override LinkedIn's page from inside its strict CSP, andworld: 'MAIN'only exists in MV3. Firefox 128+ supports it (already thestrict_min_version), so the feature now works on Firefox too. Chrome / Edge / Opera / Brave / Firefox are fully supported. CI builds all four browser targets.
Added
- Update notifier. Because the extension is installed manually (no store),
the background periodically (via
chrome.alarms) checks a public releases manifest and shows a one-time notification per version when a newer build is available, linking to the releases page. The options page shows the installed version and a "Check for updates" toggle to disable it entirely. Logic insrc/lib/update-check.ts.
Security
- The extension now makes ONE outbound network request: an anonymous update
check. It fetches a public static JSON
(
…/josedacosta-extensions-releases/…/extension.json) with no query string and no identifier, reading onlylatestVersion. This relaxes the previous "never phones home" posture; it is opt-out via thecheckForUpdatessetting and documented inPRIVACY.md. Newalarms+notificationspermissions back the feature.
Fixed
- Relationship badge now lands on the post author, not the resharer. On "X liked/commented on this" posts, the badge could attach to the reactor (e.g. a followed company) while showing the author's follow state — so it read "Non abonné" next to a page you actually follow. The 3rd-degree token is rendered "3e et +", which the degree matcher missed (length + missing "et +"), causing the fallback to the wrong actor. The matcher now accepts "3e et +" / "3rd+", so the badge anchors on the author's degree and reflects their relationship. (The notification level — the bell's "All / Most relevant / Off" — is not present in the feed HTML, so it cannot be shown per post.)
Added
- Update notifier. Because the extension is installed manually (no store),
the background periodically (via
chrome.alarms) checks a public releases manifest and shows a one-time notification per version when a newer build is available, linking to the releases page. The options page shows the installed version and a "Check for updates" toggle to disable it entirely. Logic insrc/lib/update-check.ts.
Security
- The extension now makes ONE outbound network request: an anonymous update
check. It fetches a public static JSON
(
…/josedacosta-extensions-releases/…/extension.json) with no query string and no identifier, reading onlylatestVersion. This relaxes the previous "never phones home" posture; it is opt-out via thecheckForUpdatessetting and documented inPRIVACY.md. Newalarms+notificationspermissions back the feature.
Fixed
- Relationship badge now lands on the post author, not the resharer. On "X liked/commented on this" posts, the badge could attach to the reactor (e.g. a followed company) while showing the author's follow state — so it read "Non abonné" next to a page you actually follow. The 3rd-degree token is rendered "3e et +", which the degree matcher missed (length + missing "et +"), causing the fallback to the wrong actor. The matcher now accepts "3e et +" / "3rd+", so the badge anchors on the author's degree and reflects their relationship. (The notification level — the bell's "All / Most relevant / Off" — is not present in the feed HTML, so it cannot be shown per post.)