feat(privacy+extension): publier la page /privacy et préparer les builds Chrome/Firefox Store
- Nouvelle page /privacy bilingue (FR/EN) avec sélection par Accept-Language
ou ?lang=, sections complètes (données collectées, IA/BYOK, RGPD, sécurité,
contact). Style sombre aligné sur la landing (x.ai premium).
- Liens footer landing.footer.legal.* mis à jour vers /privacy dans les 15 locales.
- Extension Web Clipper rendue cross-browser :
* background.js détecte chrome.sidePanel vs browser.sidebarAction
* sidepanel.js ajoute un shim chrome<->browser pour Firefox
* manifest.json inclut sidebar_action + browser_specific_settings.gecko
+ optional_host_permissions (Chrome les ignore)
- Script scripts/build-firefox.mjs pour produire un .xpi signé pour AMO,
symétrique de build-chrome-store.mjs. Exclusion mutuelle des artefacts
Chrome/Firefox pour éviter la cross-contamination.
- Branding corrigé : 176 occurrences de 'Momento' -> 'Memento' dans le
bundle de traductions embarqué.
- Version bumpée 0.3.1 -> 0.4.0.
This commit is contained in:
@@ -40,17 +40,24 @@ function ensureDir(dirPath) {
|
||||
}
|
||||
}
|
||||
|
||||
// Copy all files from source to destination, excluding specified patterns
|
||||
// Copy all files from source to destination, excluding specified patterns.
|
||||
// `exclude` accepts RegExp (matched against the relative path) or a function
|
||||
// returning true when the entry (relative path, isDirectory flag) should be skipped.
|
||||
function copyFiles(src, dest, exclude = []) {
|
||||
ensureDir(dest)
|
||||
const entries = fs.readdirSync(src, { withFileTypes: true })
|
||||
|
||||
const isExcluded = (relPath, isDir) =>
|
||||
exclude.some(rule => {
|
||||
if (typeof rule === 'function') return rule(relPath, isDir)
|
||||
return relPath.match(rule) !== null
|
||||
})
|
||||
|
||||
for (const entry of entries) {
|
||||
const srcPath = path.join(src, entry.name)
|
||||
const relPath = path.relative(extRoot, srcPath)
|
||||
|
||||
// Skip excluded files/directories
|
||||
if (exclude.some(pattern => relPath.match(pattern))) {
|
||||
if (isExcluded(relPath, entry.isDirectory())) {
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -85,6 +92,11 @@ function processManifestJson(content) {
|
||||
manifest.content_scripts[0].matches = ['https://memento-note.com/*', 'https://www.memento-note.com/*']
|
||||
}
|
||||
|
||||
// Strip Firefox-only fields (Chrome Store rejects unknown keys on strict review)
|
||||
delete manifest.sidebar_action
|
||||
delete manifest.browser_specific_settings
|
||||
delete manifest.optional_host_permissions
|
||||
|
||||
return JSON.stringify(manifest, null, 2)
|
||||
}
|
||||
|
||||
@@ -177,13 +189,19 @@ async function build() {
|
||||
}
|
||||
ensureDir(distDir)
|
||||
|
||||
// Copy extension files (excluding build scripts and dist)
|
||||
// Copy extension files (excluding build scripts, internal sources and dist)
|
||||
log('📋 Copying extension files...', 'blue')
|
||||
copyFiles(extRoot, distDir, [
|
||||
/^dist-/,
|
||||
/^scripts\//,
|
||||
// Top-level directories to skip entirely (do not even create empty folders)
|
||||
(rel, isDir) => isDir && /^dist-/i.test(rel),
|
||||
(rel, isDir) => isDir && (rel === 'scripts' || rel === 'i18n'),
|
||||
// File-level patterns
|
||||
/\.md$/,
|
||||
/^node_modules$/
|
||||
/^node_modules$/,
|
||||
/^diagnose\.js$/, // dev diagnostic script
|
||||
/^test-sidepanel\.html$/, // dev test page
|
||||
/^memento-web-clipper-chrome-store\.zip$/, // avoid recursive zip-in-zip
|
||||
/^memento-web-clipper-firefox\.xpi$/, // exclude sibling Firefox build artifacts
|
||||
])
|
||||
|
||||
// Process sidepanel.js
|
||||
|
||||
Reference in New Issue
Block a user