fix(extension): applySelectionFromMessage tolérant + amorçage pageUrl v0.4.2
Some checks failed
CI / Deploy production (on server) (push) Has been cancelled
CI / Lint, Unit Tests & Build (push) Has been cancelled

VRAI bug (que tu as raison de souligner, j'aurais pas dû revert) :
la fonction applySelectionFromMessage rejetait TOUT message si pageUrl
n'était pas encore settée, ou si les URLs différaient sur un trailing
slash / fragment / query. Et pageUrl se populate de façon async via
chrome.tabs.query dans refreshPageContext — donc le premier SELECTION_CHANGED
arrivait avant pageUrl, était rejeté, et la sélection n'était jamais
appliquée.

Fixes dans sidepanel.js :
- applySelectionFromMessage : si pageUrl vide, on amorce depuis le
  message (pageUrl, pageTitle, pageDomain, pageFavicon). Sans ce
  fallback, le bouton « Clipper la sélection » n'apparaît jamais.
- Comparaison d'URL tolérante (origin + pathname, sans trailing slash,
  hash, query).
- refreshPageContext : ne pas marquer tab?.url undefined comme restricted.

Version bumpée 0.4.1 → 0.4.2 (à chaque livraison comme demandé).
This commit is contained in:
Antigravity
2026-07-18 10:30:45 +00:00
parent 07ac42cbee
commit 1987b084c3
20 changed files with 147 additions and 24 deletions

View File

@@ -36,7 +36,7 @@
"message": "Fügen Sie die HTTPS- (oder LAN-)URL Ihres Memento-Servers ein. Cookies in diesem Browser verarbeiten die Anmeldung." "message": "Fügen Sie die HTTPS- (oder LAN-)URL Ihres Memento-Servers ein. Cookies in diesem Browser verarbeiten die Anmeldung."
}, },
"footerVersion": { "footerVersion": {
"message": "Memento Web Clipper 0.4.0" "message": "Memento Web Clipper 0.4.2"
}, },
"errPermissionDenied": { "errPermissionDenied": {
"message": "Memento kann nicht auf diese Registerkarte zugreifen. Überprüfen Sie die Tastatur-/Site-Erweiterungsberechtigungen oder öffnen Sie den Seitenbereich." "message": "Memento kann nicht auf diese Registerkarte zugreifen. Überprüfen Sie die Tastatur-/Site-Erweiterungsberechtigungen oder öffnen Sie den Seitenbereich."

View File

@@ -36,7 +36,7 @@
"message": "Paste the HTTPS (or LAN) URL of your Memento server. Cookies in this browser handle sign-in." "message": "Paste the HTTPS (or LAN) URL of your Memento server. Cookies in this browser handle sign-in."
}, },
"footerVersion": { "footerVersion": {
"message": "Memento Web Clipper 0.4.0" "message": "Memento Web Clipper 0.4.2"
}, },
"errPermissionDenied": { "errPermissionDenied": {
"message": "Memento can't access this tab. Check keyboard/site extension permissions — or open the Side Panel." "message": "Memento can't access this tab. Check keyboard/site extension permissions — or open the Side Panel."

View File

@@ -36,7 +36,7 @@
"message": "URL HTTPS (یا LAN) سرور Memento خود را جایگذاری کنید. کوکی‌های این مرورگر ورود به سیستم را کنترل می‌کنند." "message": "URL HTTPS (یا LAN) سرور Memento خود را جایگذاری کنید. کوکی‌های این مرورگر ورود به سیستم را کنترل می‌کنند."
}, },
"footerVersion": { "footerVersion": {
"message": "Memento Web Clipper 0.4.0" "message": "Memento Web Clipper 0.4.2"
}, },
"errPermissionDenied": { "errPermissionDenied": {
"message": "Memento نمی تواند به این برگه دسترسی پیدا کند. مجوزهای افزونه صفحه کلید/سایت را بررسی کنید - یا پانل جانبی را باز کنید." "message": "Memento نمی تواند به این برگه دسترسی پیدا کند. مجوزهای افزونه صفحه کلید/سایت را بررسی کنید - یا پانل جانبی را باز کنید."

View File

@@ -36,7 +36,7 @@
"message": "Collez l'URL HTTPS (ou LAN) de votre serveur Memento. Les cookies de ce navigateur gèrent la connexion." "message": "Collez l'URL HTTPS (ou LAN) de votre serveur Memento. Les cookies de ce navigateur gèrent la connexion."
}, },
"footerVersion": { "footerVersion": {
"message": "Memento Web Clipper 0.4.0" "message": "Memento Web Clipper 0.4.2"
}, },
"errPermissionDenied": { "errPermissionDenied": {
"message": "Memento ne peut pas accéder à cet onglet. Vérifiez les autorisations du clavier/extension de site ou ouvrez le panneau latéral." "message": "Memento ne peut pas accéder à cet onglet. Vérifiez les autorisations du clavier/extension de site ou ouvrez le panneau latéral."

View File

@@ -36,7 +36,7 @@
"message": "Fügen Sie die HTTPS- (oder LAN-)URL Ihres Memento-Servers ein. Cookies in diesem Browser verarbeiten die Anmeldung." "message": "Fügen Sie die HTTPS- (oder LAN-)URL Ihres Memento-Servers ein. Cookies in diesem Browser verarbeiten die Anmeldung."
}, },
"footerVersion": { "footerVersion": {
"message": "Memento Web Clipper 0.4.0" "message": "Memento Web Clipper 0.4.2"
}, },
"errPermissionDenied": { "errPermissionDenied": {
"message": "Memento kann nicht auf diese Registerkarte zugreifen. Überprüfen Sie die Tastatur-/Site-Erweiterungsberechtigungen oder öffnen Sie den Seitenbereich." "message": "Memento kann nicht auf diese Registerkarte zugreifen. Überprüfen Sie die Tastatur-/Site-Erweiterungsberechtigungen oder öffnen Sie den Seitenbereich."

View File

@@ -36,7 +36,7 @@
"message": "Paste the HTTPS (or LAN) URL of your Memento server. Cookies in this browser handle sign-in." "message": "Paste the HTTPS (or LAN) URL of your Memento server. Cookies in this browser handle sign-in."
}, },
"footerVersion": { "footerVersion": {
"message": "Memento Web Clipper 0.4.0" "message": "Memento Web Clipper 0.4.2"
}, },
"errPermissionDenied": { "errPermissionDenied": {
"message": "Memento can't access this tab. Check keyboard/site extension permissions — or open the Side Panel." "message": "Memento can't access this tab. Check keyboard/site extension permissions — or open the Side Panel."

View File

@@ -36,7 +36,7 @@
"message": "URL HTTPS (یا LAN) سرور Memento خود را جایگذاری کنید. کوکی‌های این مرورگر ورود به سیستم را کنترل می‌کنند." "message": "URL HTTPS (یا LAN) سرور Memento خود را جایگذاری کنید. کوکی‌های این مرورگر ورود به سیستم را کنترل می‌کنند."
}, },
"footerVersion": { "footerVersion": {
"message": "Memento Web Clipper 0.4.0" "message": "Memento Web Clipper 0.4.2"
}, },
"errPermissionDenied": { "errPermissionDenied": {
"message": "Memento نمی تواند به این برگه دسترسی پیدا کند. مجوزهای افزونه صفحه کلید/سایت را بررسی کنید - یا پانل جانبی را باز کنید." "message": "Memento نمی تواند به این برگه دسترسی پیدا کند. مجوزهای افزونه صفحه کلید/سایت را بررسی کنید - یا پانل جانبی را باز کنید."

View File

@@ -36,7 +36,7 @@
"message": "Collez l'URL HTTPS (ou LAN) de votre serveur Memento. Les cookies de ce navigateur gèrent la connexion." "message": "Collez l'URL HTTPS (ou LAN) de votre serveur Memento. Les cookies de ce navigateur gèrent la connexion."
}, },
"footerVersion": { "footerVersion": {
"message": "Memento Web Clipper 0.4.0" "message": "Memento Web Clipper 0.4.2"
}, },
"errPermissionDenied": { "errPermissionDenied": {
"message": "Memento ne peut pas accéder à cet onglet. Vérifiez les autorisations du clavier/extension de site ou ouvrez le panneau latéral." "message": "Memento ne peut pas accéder à cet onglet. Vérifiez les autorisations du clavier/extension de site ou ouvrez le panneau latéral."

View File

@@ -1,7 +1,7 @@
{ {
"manifest_version": 3, "manifest_version": 3,
"name": "__MSG_extName__", "name": "__MSG_extName__",
"version": "0.4.1", "version": "0.4.2",
"description": "__MSG_extDescription__", "description": "__MSG_extDescription__",
"default_locale": "en", "default_locale": "en",
"permissions": [ "permissions": [

View File

@@ -284,17 +284,58 @@ function updateSelectionUI() {
} }
function applySelectionFromMessage(msg) { function applySelectionFromMessage(msg) {
if (!msg || msg.url !== pageUrl) return if (!msg) return
// Comparaison d'URL tolérante : ignorer trailing slash, hash, query.
// Compare origin + pathname, pas l'URL complète.
const normalizeUrl = (u) => {
if (!u) return ''
try { return new URL(u).origin + new URL(u).pathname.replace(/\/+$/, '') }
catch { return u }
}
const msgUrl = msg.url || ''
const curUrl = pageUrl || ''
// Si pageUrl n'est pas encore set (race avec refreshPageContext),
// on accepte le premier message qui arrive et on amorce les métadonnées
// de la page depuis le message. Sans ce fallback, le bouton ne s'affiche
// jamais si chrome.tabs.query rate ou revient avant refreshPageContext.
if (!curUrl) {
if (msgUrl) {
pageUrl = msgUrl
pageTitle = msg.title || pageTitle || ''
try {
const u = new URL(msgUrl)
pageDomain = u.hostname
pageFavicon = `https://www.google.com/s2/favicons?domain=${u.hostname}&sz=32`
} catch { /* ignore */ }
} else {
return
}
} else if (normalizeUrl(msgUrl) !== normalizeUrl(curUrl)) {
// URL vraiment différente (autre onglet) — ignorer.
return
}
selectionText = msg.text || '' selectionText = msg.text || ''
if (msg.dir?.toLowerCase() === 'rtl') pageDir = 'rtl' if (msg.dir?.toLowerCase() === 'rtl') pageDir = 'rtl'
if (msg.lang) pageLang = msg.lang if (msg.lang) pageLang = msg.lang
if (state === 'idle') updateSelectionUI() if (state === 'idle') {
// Si la page card n'existait pas encore (pageUrl vide avant),
// il faut un render() complet pour qu'elle apparaisse.
if (!curUrl) {
render()
} else {
updateSelectionUI()
}
} else {
console.debug('[memento] selection updated while busy, deferring UI')
}
} }
async function refreshPageContext() { async function refreshPageContext() {
const tab = await getActiveTab() const tab = await getActiveTab()
activeTabId = tab?.id ?? null activeTabId = tab?.id ?? null
pageRestricted = isRestrictedUrl(tab?.url) // Ne marquer restricted QUE si on a vraiment une URL à analyser.
// tab?.url undefined doit être traité comme "inconnu", pas restricted.
pageRestricted = tab?.url ? isRestrictedUrl(tab.url) : false
if (!tab?.id || pageRestricted) { if (!tab?.id || pageRestricted) {
pageUrl = tab?.url || '' pageUrl = tab?.url || ''

View File

@@ -36,7 +36,7 @@
"message": "Fügen Sie die HTTPS- (oder LAN-)URL Ihres Memento-Servers ein. Cookies in diesem Browser verarbeiten die Anmeldung." "message": "Fügen Sie die HTTPS- (oder LAN-)URL Ihres Memento-Servers ein. Cookies in diesem Browser verarbeiten die Anmeldung."
}, },
"footerVersion": { "footerVersion": {
"message": "Memento Web Clipper 0.4.0" "message": "Memento Web Clipper 0.4.2"
}, },
"errPermissionDenied": { "errPermissionDenied": {
"message": "Memento kann nicht auf diese Registerkarte zugreifen. Überprüfen Sie die Tastatur-/Site-Erweiterungsberechtigungen oder öffnen Sie den Seitenbereich." "message": "Memento kann nicht auf diese Registerkarte zugreifen. Überprüfen Sie die Tastatur-/Site-Erweiterungsberechtigungen oder öffnen Sie den Seitenbereich."

View File

@@ -36,7 +36,7 @@
"message": "Paste the HTTPS (or LAN) URL of your Memento server. Cookies in this browser handle sign-in." "message": "Paste the HTTPS (or LAN) URL of your Memento server. Cookies in this browser handle sign-in."
}, },
"footerVersion": { "footerVersion": {
"message": "Memento Web Clipper 0.4.0" "message": "Memento Web Clipper 0.4.2"
}, },
"errPermissionDenied": { "errPermissionDenied": {
"message": "Memento can't access this tab. Check keyboard/site extension permissions — or open the Side Panel." "message": "Memento can't access this tab. Check keyboard/site extension permissions — or open the Side Panel."

View File

@@ -36,7 +36,7 @@
"message": "URL HTTPS (یا LAN) سرور Memento خود را جایگذاری کنید. کوکی‌های این مرورگر ورود به سیستم را کنترل می‌کنند." "message": "URL HTTPS (یا LAN) سرور Memento خود را جایگذاری کنید. کوکی‌های این مرورگر ورود به سیستم را کنترل می‌کنند."
}, },
"footerVersion": { "footerVersion": {
"message": "Memento Web Clipper 0.4.0" "message": "Memento Web Clipper 0.4.2"
}, },
"errPermissionDenied": { "errPermissionDenied": {
"message": "Memento نمی تواند به این برگه دسترسی پیدا کند. مجوزهای افزونه صفحه کلید/سایت را بررسی کنید - یا پانل جانبی را باز کنید." "message": "Memento نمی تواند به این برگه دسترسی پیدا کند. مجوزهای افزونه صفحه کلید/سایت را بررسی کنید - یا پانل جانبی را باز کنید."

View File

@@ -36,7 +36,7 @@
"message": "Collez l'URL HTTPS (ou LAN) de votre serveur Memento. Les cookies de ce navigateur gèrent la connexion." "message": "Collez l'URL HTTPS (ou LAN) de votre serveur Memento. Les cookies de ce navigateur gèrent la connexion."
}, },
"footerVersion": { "footerVersion": {
"message": "Memento Web Clipper 0.4.0" "message": "Memento Web Clipper 0.4.2"
}, },
"errPermissionDenied": { "errPermissionDenied": {
"message": "Memento ne peut pas accéder à cet onglet. Vérifiez les autorisations du clavier/extension de site ou ouvrez le panneau latéral." "message": "Memento ne peut pas accéder à cet onglet. Vérifiez les autorisations du clavier/extension de site ou ouvrez le panneau latéral."

View File

@@ -1,7 +1,7 @@
{ {
"manifest_version": 3, "manifest_version": 3,
"name": "__MSG_extName__", "name": "__MSG_extName__",
"version": "0.4.1", "version": "0.4.2",
"description": "__MSG_extDescription__", "description": "__MSG_extDescription__",
"default_locale": "en", "default_locale": "en",
"permissions": [ "permissions": [

View File

@@ -284,17 +284,58 @@ function updateSelectionUI() {
} }
function applySelectionFromMessage(msg) { function applySelectionFromMessage(msg) {
if (!msg || msg.url !== pageUrl) return if (!msg) return
// Comparaison d'URL tolérante : ignorer trailing slash, hash, query.
// Compare origin + pathname, pas l'URL complète.
const normalizeUrl = (u) => {
if (!u) return ''
try { return new URL(u).origin + new URL(u).pathname.replace(/\/+$/, '') }
catch { return u }
}
const msgUrl = msg.url || ''
const curUrl = pageUrl || ''
// Si pageUrl n'est pas encore set (race avec refreshPageContext),
// on accepte le premier message qui arrive et on amorce les métadonnées
// de la page depuis le message. Sans ce fallback, le bouton ne s'affiche
// jamais si chrome.tabs.query rate ou revient avant refreshPageContext.
if (!curUrl) {
if (msgUrl) {
pageUrl = msgUrl
pageTitle = msg.title || pageTitle || ''
try {
const u = new URL(msgUrl)
pageDomain = u.hostname
pageFavicon = `https://www.google.com/s2/favicons?domain=${u.hostname}&sz=32`
} catch { /* ignore */ }
} else {
return
}
} else if (normalizeUrl(msgUrl) !== normalizeUrl(curUrl)) {
// URL vraiment différente (autre onglet) — ignorer.
return
}
selectionText = msg.text || '' selectionText = msg.text || ''
if (msg.dir?.toLowerCase() === 'rtl') pageDir = 'rtl' if (msg.dir?.toLowerCase() === 'rtl') pageDir = 'rtl'
if (msg.lang) pageLang = msg.lang if (msg.lang) pageLang = msg.lang
if (state === 'idle') updateSelectionUI() if (state === 'idle') {
// Si la page card n'existait pas encore (pageUrl vide avant),
// il faut un render() complet pour qu'elle apparaisse.
if (!curUrl) {
render()
} else {
updateSelectionUI()
}
} else {
console.debug('[memento] selection updated while busy, deferring UI')
}
} }
async function refreshPageContext() { async function refreshPageContext() {
const tab = await getActiveTab() const tab = await getActiveTab()
activeTabId = tab?.id ?? null activeTabId = tab?.id ?? null
pageRestricted = isRestrictedUrl(tab?.url) // Ne marquer restricted QUE si on a vraiment une URL à analyser.
// tab?.url undefined doit être traité comme "inconnu", pas restricted.
pageRestricted = tab?.url ? isRestrictedUrl(tab.url) : false
if (!tab?.id || pageRestricted) { if (!tab?.id || pageRestricted) {
pageUrl = tab?.url || '' pageUrl = tab?.url || ''

View File

@@ -1,7 +1,7 @@
{ {
"manifest_version": 3, "manifest_version": 3,
"name": "__MSG_extName__", "name": "__MSG_extName__",
"version": "0.4.1", "version": "0.4.2",
"description": "__MSG_extDescription__", "description": "__MSG_extDescription__",
"default_locale": "en", "default_locale": "en",
"permissions": [ "permissions": [

View File

@@ -284,17 +284,58 @@ function updateSelectionUI() {
} }
function applySelectionFromMessage(msg) { function applySelectionFromMessage(msg) {
if (!msg || msg.url !== pageUrl) return if (!msg) return
// Comparaison d'URL tolérante : ignorer trailing slash, hash, query.
// Compare origin + pathname, pas l'URL complète.
const normalizeUrl = (u) => {
if (!u) return ''
try { return new URL(u).origin + new URL(u).pathname.replace(/\/+$/, '') }
catch { return u }
}
const msgUrl = msg.url || ''
const curUrl = pageUrl || ''
// Si pageUrl n'est pas encore set (race avec refreshPageContext),
// on accepte le premier message qui arrive et on amorce les métadonnées
// de la page depuis le message. Sans ce fallback, le bouton ne s'affiche
// jamais si chrome.tabs.query rate ou revient avant refreshPageContext.
if (!curUrl) {
if (msgUrl) {
pageUrl = msgUrl
pageTitle = msg.title || pageTitle || ''
try {
const u = new URL(msgUrl)
pageDomain = u.hostname
pageFavicon = `https://www.google.com/s2/favicons?domain=${u.hostname}&sz=32`
} catch { /* ignore */ }
} else {
return
}
} else if (normalizeUrl(msgUrl) !== normalizeUrl(curUrl)) {
// URL vraiment différente (autre onglet) — ignorer.
return
}
selectionText = msg.text || '' selectionText = msg.text || ''
if (msg.dir?.toLowerCase() === 'rtl') pageDir = 'rtl' if (msg.dir?.toLowerCase() === 'rtl') pageDir = 'rtl'
if (msg.lang) pageLang = msg.lang if (msg.lang) pageLang = msg.lang
if (state === 'idle') updateSelectionUI() if (state === 'idle') {
// Si la page card n'existait pas encore (pageUrl vide avant),
// il faut un render() complet pour qu'elle apparaisse.
if (!curUrl) {
render()
} else {
updateSelectionUI()
}
} else {
console.debug('[memento] selection updated while busy, deferring UI')
}
} }
async function refreshPageContext() { async function refreshPageContext() {
const tab = await getActiveTab() const tab = await getActiveTab()
activeTabId = tab?.id ?? null activeTabId = tab?.id ?? null
pageRestricted = isRestrictedUrl(tab?.url) // Ne marquer restricted QUE si on a vraiment une URL à analyser.
// tab?.url undefined doit être traité comme "inconnu", pas restricted.
pageRestricted = tab?.url ? isRestrictedUrl(tab.url) : false
if (!tab?.id || pageRestricted) { if (!tab?.id || pageRestricted) {
pageUrl = tab?.url || '' pageUrl = tab?.url || ''