English · Français
Une extension Chrome (Manifest V3, TypeScript) qui nettoie ton fil LinkedIn. Chaque post est soumis à Jev, le modèle de classification typée de TypeSafe AI, qui répond par des probabilités plutôt que par du texte. L'engagement bait, l'auto-promo, le hors-sujet et le sponsorisé sont repliés en une barre d'une ligne, et chaque décision est expliquée directement sur le post.
Projet indépendant, sans lien avec LinkedIn ni TypeSafe AI. Il te faut ta propre clé API TypeSafe.
- Masque le bruit – engagement bait, humble-brags, posts sponsorisés, sujets que tu bloques, et posts sans rapport avec les centres d'intérêt que tu listes.
- S'explique – les posts masqués affichent la raison et les scores, avec un bouton Afficher / Re-masquer. Les posts gardés reçoivent une ligne discrète « Gardé par Jev » avec leurs scores ; un score proche du seuil passe en orange (« Gardé de justesse »), ce qui rend le réglage des seuils très simple.
- Pastille d'activité Jev – un petit badge flottant (en bas à gauche) pulse pendant les appels. Au clic : posts analysés/masqués, appels en cours, tokens, coût estimé, latence moyenne, historique des latences et derniers verdicts.
- Préserve le scroll infini – quand presque tout est replié, le fil devient trop court pour le déclencheur « charger la suite » de LinkedIn ; lkclean le relance sans déplacer ta position de lecture.
- Rapide et pas cher – un appel API par post, toutes les questions évaluées en parallèle, verdicts mis en cache pour la session. Environ 0,042 $ par million de tokens en entrée : une session de scroll coûte une fraction de centime.
-
Un content script observe le fil (
MutationObserver) et attrape chaque post :[data-testid="mainFeed"] [role="listitem"]sur le front React actuel de LinkedIn (texte dans[data-testid="expandable-text-box"]), avec repli sur l'ancien balisagedata-id="urn:li:activity:…". Le post est identifié par un hash de son texte, LinkedIn n'exposant plus d'identifiant stable. -
L'auteur et le texte partent au service worker, qui appelle
POST https://api.typesafe.ai/v1/systemoneavec un state{ user_interests, post }et plusieurs questions typées en une seule requête :Question Type Sens engagement_baitnoulclickbait, question rhétorique finale, storytelling creux self_promonoul« thrilled to announce », humble-brag, communication d'entreprise matches_interestsnoulle post touche-t-il un de tes centres d'intérêt ? top_interestchoicelequel de tes centres d'intérêt il recoupe (affiché sur le post) blocked_topicchoicesujets à masquer d'office -
Du code ordinaire transforme les probabilités en verdict selon tes seuils (
decide()danssrc/background.ts). Aucun parsing de texte libre. -
Le content script replie ou annote le post et met la pastille à jour.
La clé API ne vit que dans le service worker : elle n'est jamais transmise à la page LinkedIn ni au content script.
Depuis une release (sans rien compiler) : télécharge lkclean-vX.Y.Z.zip depuis
la dernière release, dézippe-le,
puis dans Chrome : chrome://extensions → activer le Mode développeur →
Charger l'extension non empaquetée → choisir le dossier dézippé.
Depuis les sources :
npm install
npm run buildPuis charge le dossier dist/ de la même façon.
Clique sur l'icône de l'extension pour ouvrir les réglages : colle ta clé TypeSafe (depuis console.typesafe.ai), liste tes centres d'intérêt (un par ligne), éventuellement des sujets à bloquer, puis ajuste les seuils. Recharge LinkedIn.
Après chaque rebuild, clique sur ↻ sur la carte de l'extension dans
chrome://extensions, puis recharge l'onglet LinkedIn.
| Réglage | Défaut | Effet |
|---|---|---|
| Centres d'intérêt | vide | Si vide, seul le bruit est filtré (bait, auto-promo, sponsorisé) |
| Sujets bloqués | vide | Toujours masqués quand Jev est sûr à ≥ 60 % |
| Seuil de bruit | 70 % | Masque si P(bait) ou P(auto-promo) le dépasse |
| Seuil d'intérêt | 35 % | Masque si P(correspond aux intérêts) est en dessous |
| Masquer le sponsorisé | oui | Détection locale, sans appel API |
| Longueur minimale | 40 caractères | Les posts plus courts ne sont jamais évalués |
| Expliquer les posts gardés | oui | La ligne « Gardé par Jev » |
| Pastille d'activité | oui | Le badge flottant Jev |
| Modèle | jev-latest |
Épingle une version (ex. jev-1.13.0) une fois tes seuils réglés |
- Le nom de l'auteur et le texte de chaque post affiché dans ton fil sont envoyés à l'API TypeSafe pour classification. Rien d'autre ne quitte ton navigateur, et lkclean n'a ni serveur ni analytics.
- La clé API est stockée dans le stockage synchronisé de Chrome
(
chrome.storage.sync). - Les verdicts sont mis en cache dans
chrome.storage.sessionet effacés à la fermeture du navigateur ou à chaque changement de réglages.
npm run watch # rebuild à chaque modification
npm run typecheck # tsc --noEmit| Fichier | Rôle |
|---|---|
src/content.ts |
détection des posts, extraction, masquage/annotation, relance du scroll infini |
src/sticker.ts |
la pastille d'activité Jev (Shadow DOM) |
src/background.ts |
questions, logique de décision, cache, appels API |
src/jev.ts |
client typé minimal pour l'endpoint System One |
src/options.ts, public/options.* |
page de réglages |
Pour changer ce qui est filtré, modifie buildQuestions() et decide() dans
src/background.ts. Jev évalue chaque question indépendamment et en parallèle :
une question de plus ne coûte presque rien en latence.
Chaque push et chaque pull request est vérifié (typecheck) et compilé par GitHub
Actions (.github/workflows/build.yml) ; le zip est attaché au run. Pour publier
une release, monte la version et pousse sur main :
npm version patch --no-git-tag-version # ou minor / major
git commit -am "Release v$(node -p "require('./package.json').version")"
git pushLe workflow publie vX.Y.Z avec lkclean-vX.Y.Z.zip la première fois qu'il voit une
version sans release. package.json est la seule source de vérité : le build
recopie sa version dans le manifest.
Le DOM de LinkedIn bouge régulièrement. Les sélecteurs sont regroupés en haut de
src/content.ts (POST_SELECTORS, TEXT_SELECTORS, AUTHOR_SELECTORS).
Si aucun post n'est détecté 6 secondes après l'arrivée sur /feed, l'extension
logue dans la console de la page un bloc [lkclean] DIAGNOSTIC décrivant la
structure de la page (noms d'attributs uniquement, aucun contenu). Ce bloc suffit
pour écrire de nouveaux sélecteurs. Chaque verdict est aussi logué au niveau
Verbose de la console.
Un badge ! rouge sur l'icône signifie que le dernier appel API a échoué (clé absente ou refusée, API injoignable) ; le message est dans la pastille et dans la console.