fix(extension): sync robuste content/sidepanel, diagnostic panel, preview propre v0.4.4
Some checks failed
CI / Deploy production (on server) (push) Has been cancelled
CI / Lint, Unit Tests & Build (push) Has been cancelled

- content.js : écrit la sélection dans chrome.storage.session/local en plus du message runtime
- sidepanel.js : lit le storage en fallback, loggue chaque étape, affiche un diagnostic panel
- sidepanel.js : avertissement explicite si le content script n'est pas détecté
- sidepanel.js : sépare le rendu Markdown du résumé et l'affichage texte brut de l'extrait
- sidepanel.css : agrandit la zone de prévisualisation
- i18n : nouvelles clés contentScriptMissing, diagnosticsTitle, copyDiagnostics
- locales : correction FR (Retour, Enregistrement, Note, etc.) + version 0.4.4 partout
- extract-article.ts : nettoie textContent des métadonnées (dates, temps de lecture, sources)
- analyze/route.ts : excerpt propre via makeExcerpt
- builds Chrome Store et Firefox regénérés en 0.4.4
This commit is contained in:
Antigravity
2026-07-18 16:56:51 +00:00
parent bff060ad20
commit 0ee2afbd62
67 changed files with 1794 additions and 640 deletions

View File

@@ -17,6 +17,42 @@ export interface ExtractedArticle {
locale: ClipLocaleHint
}
const META_PATTERNS = [
// Dates/heures avec "mis à jour / updated / actualizado / aktualisiert"
/\b(mis à jour|updated at|actualizado|aktualisiert am|aggiornato alle)\b[\s:|-]*.*/gi,
// Temps de lecture
/\b(temps de lecture|reading time|lesedauer|tiempo de lectura|tempo di lettura|czas czytania|время чтения|阅读时间|読了時間)\b[\s:|-]*.*/gi,
// Lignes de type "Source - Aujourd'hui à 09:15 | mis à jour à 11:07"
/^(.*?\s[-|]\s)(aujourd[']?hui|today|hoy|heute|oggi|vandaag|dzisiaj|сегодня|今日)\b.*$/gim,
// Heures seules type "09:15" ou "09:15 | mis à jour..."
/^\s*\d{1,2}:\d{2}(\s*[-|]\s*.*)?$/gm,
]
export function cleanClipText(text: string): string {
let cleaned = text.replace(/\r\n/g, '\n').replace(/\n{3,}/g, '\n\n')
for (const pattern of META_PATTERNS) {
cleaned = cleaned.replace(pattern, '')
}
return cleaned
.split('\n')
.map((line) => line.trim())
.filter((line) => line.length > 0)
.join('\n')
.replace(/\n{3,}/g, '\n\n')
.trim()
}
export function makeExcerpt(text: string, maxLen = 500): string {
if (text.length <= maxLen) return text
// Préférer couper à la fin d'une phrase.
const sentenceEnd = text.lastIndexOf('.', maxLen)
if (sentenceEnd > maxLen * 0.6) return text.slice(0, sentenceEnd + 1).trim()
// Sinon couper au dernier espace.
const space = text.lastIndexOf(' ', maxLen)
if (space > maxLen * 0.6) return text.slice(0, space).trim() + '…'
return text.slice(0, maxLen).trim() + '…'
}
export function extractArticleFromHtml(html: string, pageUrl: string): ExtractedArticle | null {
const dom = new JSDOM(html, { url: pageUrl })
const reader = new Readability(dom.window.document)
@@ -48,11 +84,13 @@ export function extractArticleFromHtml(html: string, pageUrl: string): Extracted
const rtlBlocks = applyRtlToHtmlBlocks(sanitized, mergedLocale)
const content = wrapClipArticleHtml(rtlBlocks, mergedLocale)
const cleanText = cleanClipText(article.textContent || '')
return {
title: (article.title || '').trim(),
content,
textContent: (article.textContent || '').trim(),
excerpt: (article.excerpt || '').trim(),
textContent: cleanText,
excerpt: makeExcerpt(cleanText, 500),
locale: mergedLocale,
}
}