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:
398
memento-note/app/(public)/privacy/page.tsx
Normal file
398
memento-note/app/(public)/privacy/page.tsx
Normal file
@@ -0,0 +1,398 @@
|
||||
import { headers } from 'next/headers'
|
||||
import Link from 'next/link'
|
||||
import { parseAcceptLanguage } from '@/lib/i18n/detect-user-language'
|
||||
|
||||
export const metadata = {
|
||||
title: 'Privacy Policy — Memento',
|
||||
description: 'How Memento handles your data, the Chrome and Firefox web clipper extension, and AI features.',
|
||||
alternates: { canonical: 'https://memento-note.com/privacy' },
|
||||
robots: { index: true, follow: true },
|
||||
}
|
||||
|
||||
type Locale = 'en' | 'fr'
|
||||
const SUPPORTED: Locale[] = ['en', 'fr']
|
||||
const RTL: Locale[] = []
|
||||
|
||||
async function pickLocale(searchLang?: string | string[]): Promise<Locale> {
|
||||
// 1. Explicit ?lang= override
|
||||
const raw = Array.isArray(searchLang) ? searchLang[0] : searchLang
|
||||
if (raw && SUPPORTED.includes(raw.toLowerCase() as Locale)) return raw.toLowerCase() as Locale
|
||||
// 2. Accept-Language header
|
||||
try {
|
||||
const h = await headers()
|
||||
const accept = parseAcceptLanguage(h.get('accept-language'))
|
||||
const first = accept?.[0]?.toLowerCase()
|
||||
if (first && SUPPORTED.includes(first as Locale)) return first as Locale
|
||||
if (first && first.startsWith('fr')) return 'fr'
|
||||
} catch {
|
||||
/* fall through */
|
||||
}
|
||||
return 'en'
|
||||
}
|
||||
|
||||
type Section = { id: string; title: string; body: string[] }
|
||||
type Doc = {
|
||||
title: string
|
||||
intro: string
|
||||
lastUpdated: string
|
||||
sections: Section[]
|
||||
}
|
||||
|
||||
const DOCS: Record<Locale, Doc> = {
|
||||
fr: {
|
||||
title: 'Politique de confidentialité',
|
||||
intro:
|
||||
'Cette politique décrit comment Memento (Memento Labs) collecte, utilise et protège vos données, y compris via l\'application web, l\'extension navigateur Web Clipper et les fonctionnalités d\'intelligence artificielle. Nous concevons Memento comme un Second Brain personnel : par défaut, vos données vous appartiennent et ne sont pas partagées.',
|
||||
lastUpdated: 'Dernière mise à jour : juillet 2026',
|
||||
sections: [
|
||||
{
|
||||
id: 'data-collected',
|
||||
title: '1. Données collectées',
|
||||
body: [
|
||||
'Compte et authentification : adresse e-mail, nom facultatif, mot de passe haché (bcrypt), ou identifiant Google via OAuth (Google Auth Platform).',
|
||||
'Carnets et notes : contenu, titre, étiquettes, images jointes, pièces jointes (PDF, images, documents), métadonnées (dates, ordre, couleur, épinglé, archivé), et historique des versions (snapshots).',
|
||||
'Rappels et notifications : date/heure, récurrence, lieu facultatif, état (fait/non fait).',
|
||||
'Données IA : titre et corps des notes envoyés à votre fournisseur d\'IA configuré pour générer résumés, tags, embeddings, Memory Echo, reformulations, traduction, suggestions. Avec BYOK (votre propre clé), l\'envoi va directement vers votre fournisseur ; sinon, il transite par notre routeur IA mutualisé.',
|
||||
'Extension Web Clipper (Chrome / Firefox) : lorsque vous cliquez explicitement « Clipper », l\'extension lit l\'URL active, le titre de la page, la sélection de texte et (selon votre choix) le HTML complet de la page. Aucune lecture du contenu de page n\'a lieu tant que vous n\'initiez pas l\'action.',
|
||||
'Cookies techniques et de session : strictement nécessaires à l\'authentification (cookies NextAuth) et aux préférences (langue, thème, panneau de configuration de l\'extension). Aucun cookie publicitaire, aucun tracker tiers par défaut.',
|
||||
'Facturation et quotas : pour les tiers payants, référence de commande et jetons d\'utilisation IA stockés pour appliquer les quotas (réservation atomique). Aucune donnée bancaire (traitée par Stripe).',
|
||||
'Métadonnées techniques : adresse IP (journalisée ~30 jours à des fins anti-abus), user-agent, version de l\'extension, codes d\'erreur anonymisés.',
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'usage',
|
||||
title: '2. Usage des données',
|
||||
body: [
|
||||
'Fournir le service : synchronisation de vos carnets, recherche, Memory Echo, Memory Echo insights, agents, publication web de notes, clips depuis l\'extension.',
|
||||
'Amélioration : journaux d\'erreur anonymisés et métriques agrégées (Prometheus + dashboards Grafana internes) pour diagnostiquer les pannes et améliorer les performances.',
|
||||
'Sécurité : détection d\'abus, protection contre le credential stuffing et les dépassements de quotas.',
|
||||
'Nous ne vendons jamais vos données. Nous ne les partageons pas avec des annonceurs. Aucun profilage publicitaire.',
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'third-parties',
|
||||
title: '3. Services tiers et sous-traitants',
|
||||
body: [
|
||||
'Google OAuth : authentification optionnelle (Branding → Audience « In production »).',
|
||||
'Google Cloud Storage (GCP) : hébergement des fichiers joints et des pièces jointes, région Europe (eu-west-3 / europe-west9).',
|
||||
'PostgreSQL (Docker self-hosted) : base principale, sauvegardes chiffrées au repos.',
|
||||
'Redis (Docker self-hosted) : cache de session, rate limiting, files de tâches.',
|
||||
'Fournisseurs d\'IA (OpenAI, Anthropic, Google Gemini, Mistral, OpenRouter, ou votre propre clé BYOK) : envoi du titre et du contenu de la note sélectionnée pour les fonctionnalités IA. Aucun entraînement n\'est effectué sur vos données par notre intermédiaire ; voir la politique du fournisseur retenu.',
|
||||
'Stripe : facturation des abonnements. Les données de paiement sont gérées exclusivement par Stripe (PCI-DSS).',
|
||||
'Resend : e-mails transactionnels (vérification de compte, réinitialisation de mot de passe, alertes quota).',
|
||||
'Google Fonts (CDN fonts.googleapis.com) : polices web sur la landing page et le side panel de l\'extension — adresse IP communiquée à Google.',
|
||||
'Google Favicons (google.com/s2/favicons) : récupération d\'icônes de sites dans l\'extension — l\'URL du domaine est communiquée à Google.',
|
||||
'Grafana Cloud (monitoring optionnel) : métriques agrégées anonymisées.',
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'extension',
|
||||
title: '4. Permissions de l\'extension navigateur',
|
||||
body: [
|
||||
'activeTab : lecture du contenu de l\'onglet actif uniquement lorsque vous cliquez le bouton « Clipper ». Aucune lecture passive.',
|
||||
'scripting : injection d\'un petit script de capture de sélection après votre clic explicite.',
|
||||
'storage : mémorisation de l\'URL de votre serveur Memento et du dernier carnet utilisé.',
|
||||
'sidePanel (Chrome) / sidebar_action (Firefox) : ouverture du panneau latéral pour afficher l\'interface du clipper.',
|
||||
'tabs : récupération de l\'URL et du titre de l\'onglet actif pour le contexte du clip.',
|
||||
'host_permissions : limitées à memento-note.com en production. Les hôtes étendus (localhost / LAN) sont disponibles uniquement en build développeur.',
|
||||
'L\'extension ne lit jamais l\'historique de navigation. Elle ne communique avec aucun serveur autre que l\'instance Memento que vous avez explicitement configurée.',
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'ai',
|
||||
title: '5. Intelligence artificielle et BYOK',
|
||||
body: [
|
||||
'Les fonctionnalités IA (résumé, tags, Memory Echo, embeddings sémantiques, agents) consomment un quota mesuré en tokens. Le quota est réservé atomiquement avant chaque appel.',
|
||||
'Vous pouvez fournir votre propre clé d\'API (BYOK) pour contourner les quotas mutualisés et faire transiter les appels directement par votre fournisseur. La clé est chiffrée au repos (AES-256-GCM) et n\'est jamais affichée en clair après saisie.',
|
||||
'Aucun entraînement de modèle n\'est effectué sur vos données par notre intermédiaire.',
|
||||
'Vous pouvez désactiver individuellement chaque fonctionnalité IA dans Réglages → IA.',
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'retention',
|
||||
title: '6. Conservation des données',
|
||||
body: [
|
||||
'Carnets et notes : conservés tant que votre compte est actif. Suppression définitive dans les 30 jours suivant la suppression du compte (sauf obligation légale contraire).',
|
||||
'Corbeille : notes supprimées conservées 30 jours avant purge définitive.',
|
||||
'Journaux d\'erreur : 90 jours maximum, agrégés ensuite.',
|
||||
'Sauvegardes base de données : conservées 30 jours en rotation chiffrée.',
|
||||
'Métadonnées de facturation : conservées 10 ans (obligation comptable).',
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'rights',
|
||||
title: '7. Vos droits (RGPD / CCPA)',
|
||||
body: [
|
||||
'Accès : export complet de vos notes, carnets, étiquettes et paramètres via Réglages → Données.',
|
||||
'Rectification : édition directe dans l\'interface.',
|
||||
'Effacement : suppression de compte dans Réglages → Données. Effacement définitif sous 30 jours.',
|
||||
'Portabilité : export JSON des notes, carnets et étiquettes (équivalent de l\'outil `export_notes` du serveur MCP).',
|
||||
'Opposition au traitement IA : désactivation des fonctionnalités IA dans Réglages → IA.',
|
||||
'Réclamation : vous pouvez saisir la CNIL (France) ou l\'autorité de contrôle de votre lieu de résidence.',
|
||||
'Pour exercer vos droits : privacy@memento-note.com.',
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'security',
|
||||
title: '8. Sécurité',
|
||||
body: [
|
||||
'Transport : HTTPS obligatoire, HSTS activé, en-têtes de sécurité (CSP, X-Frame-Options, X-Content-Type-Options).',
|
||||
'Stockage : mots de passe hachés bcrypt, clés API chiffrées AES-256-GCM, secrets en variables d\'environnement chiffrées au repos.',
|
||||
'Authentification : cookies de session NextAuth, JWT signés, expiration glissante.',
|
||||
'Audit : journaux d\'audit log pour les actions sensibles (changement de mot de passe, export de données, suppression de compte).',
|
||||
'Bug bounty : security@memento-note.com pour signaler une vulnérabilité. Programme responsable de divulgation.',
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'minors',
|
||||
title: '9. Mineurs',
|
||||
body: [
|
||||
'Memento n\'est pas destiné aux enfants de moins de 16 ans (RGPD) ou 13 ans (COPPA). Nous ne collectons pas sciemment de données auprès de mineurs. Si vous pensez qu\'un mineur a fourni des données, contactez privacy@memento-note.com pour suppression.',
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'changes',
|
||||
title: '10. Modifications de cette politique',
|
||||
body: [
|
||||
'Toute modification substantielle est annoncée par e-mail au moins 30 jours à l\'avance et par bannière dans l\'application. La date de « dernière mise à jour » en haut de cette page est mise à jour à chaque révision.',
|
||||
'Les modifications non substantielles (corrections de formulation, liens mis à jour) sont publiées sans préavis. La version précédente reste accessible sur demande.',
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'contact',
|
||||
title: '11. Contact et responsable de traitement',
|
||||
body: [
|
||||
'Memento Labs — Responsable de traitement : privacy@memento-note.com',
|
||||
'Délégué à la protection des données (DPO) : dpo@memento-note.com',
|
||||
'Adresse postale : Memento Labs, [adresse], France',
|
||||
'Hébergeur des données : serveur self-hosted sur infrastructure Memento Labs (PostgreSQL + Redis Docker, région Europe).',
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
en: {
|
||||
title: 'Privacy Policy',
|
||||
intro:
|
||||
'This policy describes how Memento (Memento Labs) collects, uses and protects your data, including through the web app, the Web Clipper browser extension, and AI features. We design Memento as a personal Second Brain: by default your data belongs to you and is not shared.',
|
||||
lastUpdated: 'Last updated: July 2026',
|
||||
sections: [
|
||||
{
|
||||
id: 'data-collected',
|
||||
title: '1. Data we collect',
|
||||
body: [
|
||||
'Account and authentication: email address, optional name, hashed password (bcrypt), or Google identifier via OAuth (Google Auth Platform).',
|
||||
'Notebooks and notes: content, title, labels, attached images and files (PDF, images, documents), metadata (dates, order, color, pinned, archived), and version history (snapshots).',
|
||||
'Reminders and notifications: date/time, recurrence, optional location, status (done/not done).',
|
||||
'AI data: note title and body sent to your configured AI provider to generate summaries, tags, embeddings, Memory Echo, reformulations, translations, suggestions. With BYOK (your own key), requests go directly to your provider; otherwise they transit through our shared AI router.',
|
||||
'Web Clipper extension (Chrome / Firefox): when you explicitly click "Clip", the extension reads the active URL, page title, text selection and (depending on your choice) the full page HTML. No page content is read unless you initiate the action.',
|
||||
'Technical and session cookies: strictly necessary for authentication (NextAuth cookies) and preferences (language, theme, extension settings panel). No advertising cookies, no third-party trackers by default.',
|
||||
'Billing and quotas: for paid tiers, order reference and AI usage tokens stored to enforce quotas (atomic reservation). No bank card data (handled by Stripe).',
|
||||
'Technical metadata: IP address (logged ~30 days for anti-abuse), user agent, extension version, anonymized error codes.',
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'usage',
|
||||
title: '2. How we use data',
|
||||
body: [
|
||||
'Provide the service: notebook sync, search, Memory Echo, Memory Echo insights, agents, web publishing of notes, clips from the extension.',
|
||||
'Improvement: anonymized error logs and aggregated metrics (internal Prometheus + Grafana dashboards) to diagnose failures and improve performance.',
|
||||
'Security: abuse detection, protection against credential stuffing and quota overruns.',
|
||||
'We never sell your data. We never share it with advertisers. No advertising profiling.',
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'third-parties',
|
||||
title: '3. Third parties and sub-processors',
|
||||
body: [
|
||||
'Google OAuth: optional authentication (Branding → Audience "In production").',
|
||||
'Google Cloud Storage (GCP): hosting of attached files and attachments, Europe region (eu-west-3 / europe-west9).',
|
||||
'PostgreSQL (Docker self-hosted): main database, encrypted backups at rest.',
|
||||
'Redis (Docker self-hosted): session cache, rate limiting, job queues.',
|
||||
'AI providers (OpenAI, Anthropic, Google Gemini, Mistral, OpenRouter, or your own BYOK key): the title and content of the selected note are sent for AI features. No training is performed on your data through us; see the chosen provider\'s policy.',
|
||||
'Stripe: subscription billing. Payment data is handled exclusively by Stripe (PCI-DSS).',
|
||||
'Resend: transactional emails (account verification, password reset, quota alerts).',
|
||||
'Google Fonts (CDN fonts.googleapis.com): web fonts on the landing page and the extension side panel — IP address is shared with Google.',
|
||||
'Google Favicons (google.com/s2/favicons): site icon retrieval in the extension — the domain URL is shared with Google.',
|
||||
'Grafana Cloud (optional monitoring): anonymized aggregated metrics.',
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'extension',
|
||||
title: '4. Browser extension permissions',
|
||||
body: [
|
||||
'activeTab: read the active tab content only when you click the "Clip" button. No passive reading.',
|
||||
'scripting: inject a small selection-capture script after your explicit click.',
|
||||
'storage: remember your Memento server URL and the last-used notebook.',
|
||||
'sidePanel (Chrome) / sidebar_action (Firefox): open the side panel to display the clipper UI.',
|
||||
'tabs: retrieve the active tab URL and title for the clip context.',
|
||||
'host_permissions: limited to memento-note.com in production. Extended hosts (localhost / LAN) are only available in the developer build.',
|
||||
'The extension never reads your browsing history. It never communicates with any server other than the Memento instance you explicitly configured.',
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'ai',
|
||||
title: '5. Artificial intelligence and BYOK',
|
||||
body: [
|
||||
'AI features (summary, tags, Memory Echo, semantic embeddings, agents) consume a token-based quota. The quota is atomically reserved before each call.',
|
||||
'You can provide your own API key (BYOK) to bypass shared quotas and route calls directly through your provider. The key is encrypted at rest (AES-256-GCM) and is never displayed in clear text after entry.',
|
||||
'No model training is performed on your data through us.',
|
||||
'You can disable each AI feature individually in Settings → AI.',
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'retention',
|
||||
title: '6. Data retention',
|
||||
body: [
|
||||
'Notebooks and notes: kept as long as your account is active. Permanently deleted within 30 days of account deletion (unless a legal obligation requires otherwise).',
|
||||
'Trash: deleted notes kept for 30 days before permanent purge.',
|
||||
'Error logs: maximum 90 days, then aggregated.',
|
||||
'Database backups: kept 30 days in encrypted rotation.',
|
||||
'Billing metadata: kept 10 years (accounting obligation).',
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'rights',
|
||||
title: '7. Your rights (GDPR / CCPA)',
|
||||
body: [
|
||||
'Access: full export of your notes, notebooks, labels and settings via Settings → Data.',
|
||||
'Rectification: edit directly in the interface.',
|
||||
'Erasure: account deletion in Settings → Data. Definitive erasure within 30 days.',
|
||||
'Portability: JSON export of notes, notebooks and labels (equivalent of the `export_notes` MCP server tool).',
|
||||
'Opt-out of AI processing: disable AI features in Settings → AI.',
|
||||
'Complaint: you may contact the CNIL (France) or the supervisory authority of your place of residence.',
|
||||
'To exercise your rights: privacy@memento-note.com.',
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'security',
|
||||
title: '8. Security',
|
||||
body: [
|
||||
'Transport: HTTPS enforced, HSTS enabled, security headers (CSP, X-Frame-Options, X-Content-Type-Options).',
|
||||
'Storage: bcrypt-hashed passwords, AES-256-GCM-encrypted API keys, secrets in encrypted environment variables at rest.',
|
||||
'Authentication: NextAuth session cookies, signed JWT, sliding expiration.',
|
||||
'Audit: audit logs for sensitive actions (password change, data export, account deletion).',
|
||||
'Bug bounty: security@memento-note.com to report a vulnerability. Responsible disclosure program.',
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'minors',
|
||||
title: '9. Minors',
|
||||
body: [
|
||||
'Memento is not intended for children under 16 (GDPR) or 13 (COPPA). We do not knowingly collect data from minors. If you believe a minor has provided data, contact privacy@memento-note.com for deletion.',
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'changes',
|
||||
title: '10. Changes to this policy',
|
||||
body: [
|
||||
'Any substantial change is announced by email at least 30 days in advance and via an in-app banner. The "Last updated" date at the top of this page is updated with each revision.',
|
||||
'Non-substantial changes (wording fixes, updated links) are published without notice. The previous version remains accessible on request.',
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'contact',
|
||||
title: '11. Contact and data controller',
|
||||
body: [
|
||||
'Memento Labs — Data controller: privacy@memento-note.com',
|
||||
'Data Protection Officer (DPO): dpo@memento-note.com',
|
||||
'Postal address: Memento Labs, [address], France',
|
||||
'Data hosting: self-hosted on Memento Labs infrastructure (PostgreSQL + Redis Docker, Europe region).',
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
export default async function PrivacyPage({
|
||||
searchParams,
|
||||
}: {
|
||||
searchParams?: Promise<{ lang?: string | string[] }>
|
||||
}) {
|
||||
const sp = searchParams ? (await searchParams) : undefined
|
||||
const locale = await pickLocale(sp?.lang)
|
||||
const doc = DOCS[locale]
|
||||
const isRtl = RTL.includes(locale)
|
||||
|
||||
return (
|
||||
<main
|
||||
lang={locale}
|
||||
dir={isRtl ? 'rtl' : 'ltr'}
|
||||
className="min-h-screen bg-[#0B0A09] text-[#F4F1EA] font-[family-name:var(--font-manrope)] selection:bg-[#D4A373]/40 selection:text-white"
|
||||
>
|
||||
<header className="px-5 sm:px-8 py-6 border-b border-white/[0.06] flex items-center justify-between">
|
||||
<Link href="/" className="flex items-center gap-2.5">
|
||||
<div className="w-9 h-9 bg-[#F4F1EA] text-[#0B0A09] flex items-center justify-center rounded-lg">
|
||||
<span className="font-serif text-xl font-bold leading-none">M</span>
|
||||
</div>
|
||||
<span className="font-serif text-xl font-medium tracking-tight">Memento</span>
|
||||
</Link>
|
||||
<nav className="flex items-center gap-3">
|
||||
<a
|
||||
href="?lang=fr"
|
||||
aria-current={locale === 'fr' ? 'true' : undefined}
|
||||
className={`text-xs uppercase tracking-widest px-3 py-1 rounded-full border transition-colors ${
|
||||
locale === 'fr'
|
||||
? 'bg-[#F4F1EA] text-[#0B0A09] border-[#F4F1EA]'
|
||||
: 'border-white/15 text-white/55 hover:border-white/40 hover:text-white'
|
||||
}`}
|
||||
>
|
||||
FR
|
||||
</a>
|
||||
<a
|
||||
href="?lang=en"
|
||||
aria-current={locale === 'en' ? 'true' : undefined}
|
||||
className={`text-xs uppercase tracking-widest px-3 py-1 rounded-full border transition-colors ${
|
||||
locale === 'en'
|
||||
? 'bg-[#F4F1EA] text-[#0B0A09] border-[#F4F1EA]'
|
||||
: 'border-white/15 text-white/55 hover:border-white/40 hover:text-white'
|
||||
}`}
|
||||
>
|
||||
EN
|
||||
</a>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
<article className="max-w-3xl mx-auto px-5 sm:px-8 py-16 sm:py-24">
|
||||
<p className="text-[11px] uppercase tracking-[0.3em] text-[#D4A373] mb-4">Privacy</p>
|
||||
<h1 className="font-serif text-4xl sm:text-5xl tracking-tight mb-4">{doc.title}</h1>
|
||||
<p className="text-sm text-white/40 mb-3">{doc.lastUpdated}</p>
|
||||
<p className="text-white/65 leading-relaxed text-lg mb-12">{doc.intro}</p>
|
||||
|
||||
<nav aria-label="Sommaire" className="mb-12 p-5 rounded-xl bg-white/[0.03] border border-white/[0.06]">
|
||||
<p className="text-[11px] uppercase tracking-[0.2em] text-white/40 mb-3">
|
||||
{locale === 'fr' ? 'Sommaire' : 'Contents'}
|
||||
</p>
|
||||
<ul className="space-y-1.5 text-sm">
|
||||
{doc.sections.map((s) => (
|
||||
<li key={s.id}>
|
||||
<a href={`#${s.id}`} className="text-white/65 hover:text-[#D4A373] transition-colors">
|
||||
{s.title}
|
||||
</a>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</nav>
|
||||
|
||||
<div className="space-y-12">
|
||||
{doc.sections.map((s) => (
|
||||
<section key={s.id} id={s.id} className="scroll-mt-24">
|
||||
<h2 className="font-serif text-2xl tracking-tight mb-4 text-[#F4F1EA]">{s.title}</h2>
|
||||
<div className="space-y-3 text-white/70 leading-relaxed">
|
||||
{s.body.map((p, i) => (
|
||||
<p key={i}>{p}</p>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<footer className="mt-20 pt-8 border-t border-white/[0.06] text-xs text-white/35 flex flex-wrap items-center justify-between gap-4">
|
||||
<span>© 2026 Memento Labs</span>
|
||||
<Link href="/" className="hover:text-white transition-colors">
|
||||
{locale === 'fr' ? 'Retour à l\'accueil' : 'Back to home'}
|
||||
</Link>
|
||||
</footer>
|
||||
</article>
|
||||
</main>
|
||||
)
|
||||
}
|
||||
@@ -568,7 +568,11 @@ export function LandingPage() {
|
||||
if (!label || label.startsWith('landing.')) return null
|
||||
return (
|
||||
<li key={j}>
|
||||
{href.startsWith('/') ? (
|
||||
<Link href={href} className="hover:text-white transition-colors">{label}</Link>
|
||||
) : (
|
||||
<a href={href} className="hover:text-white transition-colors">{label}</a>
|
||||
)}
|
||||
</li>
|
||||
)
|
||||
})}
|
||||
|
||||
@@ -10,12 +10,22 @@ Fichiers : `extension/_locales/<lang>/messages.json`. Régénération : `node ex
|
||||
|
||||
## Installation (dev)
|
||||
|
||||
1. Chrome → `chrome://extensions`
|
||||
### Chrome
|
||||
1. `chrome://extensions`
|
||||
2. **Mode développeur** → **Charger l’extension non empaquetée** → dossier `memento-note/extension`
|
||||
3. Épingle l’icône Memento
|
||||
|
||||
> Chrome **114+** requis (Side Panel API).
|
||||
|
||||
### Firefox
|
||||
```bash
|
||||
# Avec web-ext (https://extensionworkshop.com/)
|
||||
npx web-ext run --source-dir memento-note/extension
|
||||
```
|
||||
Ou via `about:debugging` → **Ce Firefox** → **Charger un module temporaire** → sélectionner `manifest.json`.
|
||||
|
||||
> Firefox **115+** requis (MV3 + sidebarAction).
|
||||
|
||||
## Instance Memento
|
||||
|
||||
- **Dev** : icône ⚙ → URL (`http://localhost:3000` ou IP LAN) → **Appliquer & reconnecter**
|
||||
@@ -45,6 +55,30 @@ Fichiers : `extension/_locales/<lang>/messages.json`. Régénération : `node ex
|
||||
|
||||
Détection automatique `dir` / `lang` (ex. BBC Persian), aperçu RTL avec Vazirmatn.
|
||||
|
||||
## Builds production
|
||||
|
||||
Deux scripts produisent des paquets prêts à publier :
|
||||
|
||||
```bash
|
||||
# Chrome Web Store (zip)
|
||||
node extension/scripts/build-chrome-store.mjs
|
||||
# → memento-note/extension/memento-web-clipper-chrome-store.zip (~44 Ko, 27 fichiers)
|
||||
|
||||
# Firefox Add-ons (xpi)
|
||||
node extension/scripts/build-firefox.mjs
|
||||
# → memento-note/extension/memento-web-clipper-firefox.xpi (~44 Ko, 27 fichiers)
|
||||
```
|
||||
|
||||
Chaque build :
|
||||
- Verrouille `ALLOW_INSTANCE_CONFIG = false` (URL prod en dur)
|
||||
- Restreint les `host_permissions` à `https://memento-note.com/*`
|
||||
- Génère les PNG d'icônes depuis `public/icons/icon-{192,512}.svg`
|
||||
- Exclut les fichiers internes (`i18n/`, `diagnose.js`, `test-sidepanel.html`, etc.)
|
||||
|
||||
Différences manifest :
|
||||
- **Chrome** : `side_panel` (Side Panel API Chrome 114+)
|
||||
- **Firefox** : `sidebar_action` + `browser_specific_settings.gecko` (Firefox 115+)
|
||||
|
||||
## APIs
|
||||
|
||||
- `GET /api/clip/notebooks`
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
"message": "Fügen Sie die HTTPS- (oder LAN-)URL Ihres Memento-Servers ein. Cookies in diesem Browser verarbeiten die Anmeldung."
|
||||
},
|
||||
"footerVersion": {
|
||||
"message": "Memento Web Clipper 0.3.1"
|
||||
"message": "Memento Web Clipper 0.4.0"
|
||||
},
|
||||
"errPermissionDenied": {
|
||||
"message": "Memento kann nicht auf diese Registerkarte zugreifen. Überprüfen Sie die Tastatur-/Site-Erweiterungsberechtigungen – oder öffnen Sie den Seitenbereich."
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
"message": "Paste the HTTPS (or LAN) URL of your Memento server. Cookies in this browser handle sign-in."
|
||||
},
|
||||
"footerVersion": {
|
||||
"message": "Memento Web Clipper 0.3.1"
|
||||
"message": "Memento Web Clipper 0.4.0"
|
||||
},
|
||||
"errPermissionDenied": {
|
||||
"message": "Memento can't access this tab. Check keyboard/site extension permissions — or open the Side Panel."
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
"message": "URL HTTPS (یا LAN) سرور Memento خود را جایگذاری کنید. کوکیهای این مرورگر ورود به سیستم را کنترل میکنند."
|
||||
},
|
||||
"footerVersion": {
|
||||
"message": "Memento Web Clipper 0.3.1"
|
||||
"message": "Memento Web Clipper 0.4.0"
|
||||
},
|
||||
"errPermissionDenied": {
|
||||
"message": "Memento نمی تواند به این برگه دسترسی پیدا کند. مجوزهای افزونه صفحه کلید/سایت را بررسی کنید - یا پانل جانبی را باز کنید."
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
"message": "Clipper vers Memento"
|
||||
},
|
||||
"webClipper": {
|
||||
"message": "Web Clipper"
|
||||
"message": "Tondeuse Web"
|
||||
},
|
||||
"connected": {
|
||||
"message": "Connecté"
|
||||
@@ -36,7 +36,7 @@
|
||||
"message": "Collez l'URL HTTPS (ou LAN) de votre serveur Memento. Les cookies de ce navigateur gèrent la connexion."
|
||||
},
|
||||
"footerVersion": {
|
||||
"message": "Memento Web Clipper 0.3.1"
|
||||
"message": "Memento Web Clipper 0.4.0"
|
||||
},
|
||||
"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."
|
||||
@@ -78,13 +78,13 @@
|
||||
"message": "Enregistrer le lien uniquement"
|
||||
},
|
||||
"pageNotAccessible": {
|
||||
"message": "Impossible de clipper ici — cette page bloque l'accès aux extensions."
|
||||
"message": "Impossible de découper ici : cette page bloque l'accès aux extensions."
|
||||
},
|
||||
"errLoginRequired": {
|
||||
"message": "Veuillez d'abord vous connecter à Memento dans ce navigateur."
|
||||
},
|
||||
"errLoadNotebooks": {
|
||||
"message": "Impossible de charger les carnets. Essayez de vous reconnecter."
|
||||
"message": "Impossible de charger les blocs-notes. Essayez de vous reconnecter."
|
||||
},
|
||||
"notebooksLoaded": {
|
||||
"message": "Carnets chargés"
|
||||
@@ -123,22 +123,22 @@
|
||||
"message": "Enregistrer dans Memento"
|
||||
},
|
||||
"back": {
|
||||
"message": "Retour"
|
||||
"message": "Dos"
|
||||
},
|
||||
"analyzingSource": {
|
||||
"message": "Analyse de la source"
|
||||
},
|
||||
"statusAnalyzing": {
|
||||
"message": "Analyse…"
|
||||
"message": "Analyser…"
|
||||
},
|
||||
"statusSaving": {
|
||||
"message": "Enregistrement…"
|
||||
"message": "Économie…"
|
||||
},
|
||||
"processingDetail": {
|
||||
"message": "Génération automatique des tags, résumé sémantique et calcul des embeddings."
|
||||
},
|
||||
"noteSaved": {
|
||||
"message": "Note enregistrée"
|
||||
"message": "Remarque enregistrée"
|
||||
},
|
||||
"sentToNotebook": {
|
||||
"message": "Enregistré dans $NOTEBOOK$",
|
||||
@@ -165,7 +165,7 @@
|
||||
"message": "Réessayer"
|
||||
},
|
||||
"errNoSelection": {
|
||||
"message": "Sélectionnez d'abord du texte, ou clippez la page entière."
|
||||
"message": "Sélectionnez d’abord le texte ou coupez la page entière."
|
||||
},
|
||||
"errAnalyzeFailed": {
|
||||
"message": "Impossible d'analyser cette page."
|
||||
@@ -177,6 +177,6 @@
|
||||
"message": "Problème de réseau : vérifiez votre connexion et l'URL Memento."
|
||||
},
|
||||
"bannerPickText": {
|
||||
"message": "Surlignez le texte à clipper"
|
||||
"message": "Mettez en surbrillance le texte sur la page ou coupez la page entière."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,23 @@
|
||||
/** Service worker — ouvre le panneau latéral au clic sur l’icône. */
|
||||
chrome.runtime.onInstalled.addListener(() => {
|
||||
chrome.sidePanel.setPanelBehavior({ openPanelOnActionClick: true }).catch(() => {})
|
||||
})
|
||||
/** Service worker — ouvre le panneau latéral (Chrome Side Panel ou Firefox sidebar) au clic sur l'icône. */
|
||||
const extApi = (typeof browser !== 'undefined' && browser.sidebarAction) ? browser : chrome
|
||||
|
||||
chrome.runtime.onStartup.addListener(() => {
|
||||
chrome.sidePanel.setPanelBehavior({ openPanelOnActionClick: true }).catch(() => {})
|
||||
if (extApi.sidePanel?.setPanelBehavior) {
|
||||
// Chrome MV3 : Side Panel API
|
||||
extApi.sidePanel.setPanelBehavior({ openPanelOnActionClick: true }).catch(() => {})
|
||||
} else if (extApi.sidebarAction?.open) {
|
||||
// Firefox MV3 : sidebarAction
|
||||
extApi.action?.onClicked?.addListener?.(() => {
|
||||
extApi.sidebarAction.open().catch(() => {})
|
||||
})
|
||||
}
|
||||
|
||||
extApi.runtime?.onInstalled?.addListener?.(() => {
|
||||
if (extApi.sidePanel?.setPanelBehavior) {
|
||||
extApi.sidePanel.setPanelBehavior({ openPanelOnActionClick: true }).catch(() => {})
|
||||
}
|
||||
})
|
||||
extApi.runtime?.onStartup?.addListener?.(() => {
|
||||
if (extApi.sidePanel?.setPanelBehavior) {
|
||||
extApi.sidePanel.setPanelBehavior({ openPanelOnActionClick: true }).catch(() => {})
|
||||
}
|
||||
})
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
"message": "Fügen Sie die HTTPS- (oder LAN-)URL Ihres Memento-Servers ein. Cookies in diesem Browser verarbeiten die Anmeldung."
|
||||
},
|
||||
"footerVersion": {
|
||||
"message": "Memento Web Clipper 0.3.1"
|
||||
"message": "Memento Web Clipper 0.4.0"
|
||||
},
|
||||
"errPermissionDenied": {
|
||||
"message": "Memento kann nicht auf diese Registerkarte zugreifen. Überprüfen Sie die Tastatur-/Site-Erweiterungsberechtigungen – oder öffnen Sie den Seitenbereich."
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
"message": "Paste the HTTPS (or LAN) URL of your Memento server. Cookies in this browser handle sign-in."
|
||||
},
|
||||
"footerVersion": {
|
||||
"message": "Memento Web Clipper 0.3.1"
|
||||
"message": "Memento Web Clipper 0.4.0"
|
||||
},
|
||||
"errPermissionDenied": {
|
||||
"message": "Memento can't access this tab. Check keyboard/site extension permissions — or open the Side Panel."
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
"message": "URL HTTPS (یا LAN) سرور Memento خود را جایگذاری کنید. کوکیهای این مرورگر ورود به سیستم را کنترل میکنند."
|
||||
},
|
||||
"footerVersion": {
|
||||
"message": "Memento Web Clipper 0.3.1"
|
||||
"message": "Memento Web Clipper 0.4.0"
|
||||
},
|
||||
"errPermissionDenied": {
|
||||
"message": "Memento نمی تواند به این برگه دسترسی پیدا کند. مجوزهای افزونه صفحه کلید/سایت را بررسی کنید - یا پانل جانبی را باز کنید."
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
"message": "Clipper vers Memento"
|
||||
},
|
||||
"webClipper": {
|
||||
"message": "Web Clipper"
|
||||
"message": "Tondeuse Web"
|
||||
},
|
||||
"connected": {
|
||||
"message": "Connecté"
|
||||
@@ -36,7 +36,7 @@
|
||||
"message": "Collez l'URL HTTPS (ou LAN) de votre serveur Memento. Les cookies de ce navigateur gèrent la connexion."
|
||||
},
|
||||
"footerVersion": {
|
||||
"message": "Memento Web Clipper 0.3.1"
|
||||
"message": "Memento Web Clipper 0.4.0"
|
||||
},
|
||||
"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."
|
||||
@@ -78,13 +78,13 @@
|
||||
"message": "Enregistrer le lien uniquement"
|
||||
},
|
||||
"pageNotAccessible": {
|
||||
"message": "Impossible de clipper ici — cette page bloque l'accès aux extensions."
|
||||
"message": "Impossible de découper ici : cette page bloque l'accès aux extensions."
|
||||
},
|
||||
"errLoginRequired": {
|
||||
"message": "Veuillez d'abord vous connecter à Memento dans ce navigateur."
|
||||
},
|
||||
"errLoadNotebooks": {
|
||||
"message": "Impossible de charger les carnets. Essayez de vous reconnecter."
|
||||
"message": "Impossible de charger les blocs-notes. Essayez de vous reconnecter."
|
||||
},
|
||||
"notebooksLoaded": {
|
||||
"message": "Carnets chargés"
|
||||
@@ -123,22 +123,22 @@
|
||||
"message": "Enregistrer dans Memento"
|
||||
},
|
||||
"back": {
|
||||
"message": "Retour"
|
||||
"message": "Dos"
|
||||
},
|
||||
"analyzingSource": {
|
||||
"message": "Analyse de la source"
|
||||
},
|
||||
"statusAnalyzing": {
|
||||
"message": "Analyse…"
|
||||
"message": "Analyser…"
|
||||
},
|
||||
"statusSaving": {
|
||||
"message": "Enregistrement…"
|
||||
"message": "Économie…"
|
||||
},
|
||||
"processingDetail": {
|
||||
"message": "Génération automatique des tags, résumé sémantique et calcul des embeddings."
|
||||
},
|
||||
"noteSaved": {
|
||||
"message": "Note enregistrée"
|
||||
"message": "Remarque enregistrée"
|
||||
},
|
||||
"sentToNotebook": {
|
||||
"message": "Enregistré dans $NOTEBOOK$",
|
||||
@@ -165,7 +165,7 @@
|
||||
"message": "Réessayer"
|
||||
},
|
||||
"errNoSelection": {
|
||||
"message": "Sélectionnez d'abord du texte, ou clippez la page entière."
|
||||
"message": "Sélectionnez d’abord le texte ou coupez la page entière."
|
||||
},
|
||||
"errAnalyzeFailed": {
|
||||
"message": "Impossible d'analyser cette page."
|
||||
@@ -177,6 +177,6 @@
|
||||
"message": "Problème de réseau : vérifiez votre connexion et l'URL Memento."
|
||||
},
|
||||
"bannerPickText": {
|
||||
"message": "Surlignez le texte à clipper"
|
||||
"message": "Mettez en surbrillance le texte sur la page ou coupez la page entière."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,23 @@
|
||||
/** Service worker — ouvre le panneau latéral au clic sur l’icône. */
|
||||
chrome.runtime.onInstalled.addListener(() => {
|
||||
chrome.sidePanel.setPanelBehavior({ openPanelOnActionClick: true }).catch(() => {})
|
||||
})
|
||||
/** Service worker — ouvre le panneau latéral (Chrome Side Panel ou Firefox sidebar) au clic sur l'icône. */
|
||||
const extApi = (typeof browser !== 'undefined' && browser.sidebarAction) ? browser : chrome
|
||||
|
||||
chrome.runtime.onStartup.addListener(() => {
|
||||
chrome.sidePanel.setPanelBehavior({ openPanelOnActionClick: true }).catch(() => {})
|
||||
if (extApi.sidePanel?.setPanelBehavior) {
|
||||
// Chrome MV3 : Side Panel API
|
||||
extApi.sidePanel.setPanelBehavior({ openPanelOnActionClick: true }).catch(() => {})
|
||||
} else if (extApi.sidebarAction?.open) {
|
||||
// Firefox MV3 : sidebarAction
|
||||
extApi.action?.onClicked?.addListener?.(() => {
|
||||
extApi.sidebarAction.open().catch(() => {})
|
||||
})
|
||||
}
|
||||
|
||||
extApi.runtime?.onInstalled?.addListener?.(() => {
|
||||
if (extApi.sidePanel?.setPanelBehavior) {
|
||||
extApi.sidePanel.setPanelBehavior({ openPanelOnActionClick: true }).catch(() => {})
|
||||
}
|
||||
})
|
||||
extApi.runtime?.onStartup?.addListener?.(() => {
|
||||
if (extApi.sidePanel?.setPanelBehavior) {
|
||||
extApi.sidePanel.setPanelBehavior({ openPanelOnActionClick: true }).catch(() => {})
|
||||
}
|
||||
})
|
||||
|
||||
@@ -1,160 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* Script de diagnostic pour l'extension Memento
|
||||
* Vérifie tous les fichiers et identifie les problèmes potentiels
|
||||
*/
|
||||
|
||||
import fs from 'fs'
|
||||
import path from 'path'
|
||||
import { fileURLToPath } from 'url'
|
||||
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url))
|
||||
const extDir = __dirname
|
||||
|
||||
console.log('🔍 Diagnostic Extension Memento\n')
|
||||
|
||||
const issues = []
|
||||
const warnings = []
|
||||
|
||||
// Vérifier la syntaxe des fichiers JS
|
||||
function checkSyntax(filePath) {
|
||||
try {
|
||||
const content = fs.readFileSync(filePath, 'utf8')
|
||||
// Pas de vérification syntaxique simple en Node.js sans eval
|
||||
// On vérifie juste que le fichier est lisible
|
||||
return true
|
||||
} catch (error) {
|
||||
issues.push(`Fichier illisible: ${filePath} - ${error.message}`)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// Vérifier les event handlers inline dans le HTML
|
||||
function checkInlineHandlers(htmlPath) {
|
||||
try {
|
||||
const content = fs.readFileSync(htmlPath, 'utf8')
|
||||
const inlineHandlers = []
|
||||
|
||||
if (content.match(/onerror=/i)) inlineHandlers.push('onerror')
|
||||
if (content.match(/onclick=/i)) inlineHandlers.push('onclick')
|
||||
if (content.match(/onload=/i)) inlineHandlers.push('onload')
|
||||
|
||||
if (inlineHandlers.length > 0) {
|
||||
issues.push(`Event handlers inline trouvés dans ${htmlPath}: ${inlineHandlers.join(', ')}`)
|
||||
} else {
|
||||
console.log('✓ Pas d\'event handlers inline dans le HTML')
|
||||
}
|
||||
} catch (error) {
|
||||
issues.push(`Impossible de lire ${htmlPath}: ${error.message}`)
|
||||
}
|
||||
}
|
||||
|
||||
// Vérifier les fixes CSP dans sidepanel.js
|
||||
function checkCSPFixes(jsPath) {
|
||||
try {
|
||||
const content = fs.readFileSync(jsPath, 'utf8')
|
||||
|
||||
// Vérifier l'absence de onerror inline
|
||||
if (content.match(/onerror=/)) {
|
||||
issues.push('Event handler onerror trouvé dans sidepanel.js')
|
||||
} else {
|
||||
console.log('✓ Pas de onerror inline dans sidepanel.js')
|
||||
}
|
||||
|
||||
// Vérifier la présence du fix avec data-fallback
|
||||
if (content.includes('data-fallback')) {
|
||||
console.log('✓ Fix CSP data-favicon présent')
|
||||
} else {
|
||||
warnings.push('Fix CSP data-favicon可能缺失')
|
||||
}
|
||||
|
||||
// Vérifier le handler de favicon
|
||||
if (content.includes("querySelector('.page-favicon')")) {
|
||||
console.log('✓ Handler error pour favicon présent')
|
||||
} else {
|
||||
warnings.push('Handler error pour favicon可能缺失')
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
issues.push(`Impossible de lire ${jsPath}: ${error.message}`)
|
||||
}
|
||||
}
|
||||
|
||||
// Vérifier le fix pick mode
|
||||
function checkPickModeFix(jsPath) {
|
||||
try {
|
||||
const content = fs.readFileSync(jsPath, 'utf8')
|
||||
|
||||
// Vérifier le handler visibilitychange
|
||||
if (content.includes('visibilityState === \'hidden\'')) {
|
||||
console.log('✓ Handler visibilitychange pour hidden présent')
|
||||
} else {
|
||||
issues.push('Handler visibilitychange pour hidden manquant')
|
||||
}
|
||||
|
||||
// Vérifier l'appel à setPickModeOnTab(false)
|
||||
if (content.match(/visibilityState === 'hidden'.*setPickModeOnTab\(false\)/s)) {
|
||||
console.log('✓ Appel setPickModeOnTab(false) dans visibilitychange présent')
|
||||
} else {
|
||||
issues.push('Appel setPickModeOnTab(false) dans visibilitychange可能缺失')
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
issues.push(`Impossible de lire ${jsPath}: ${error.message}`)
|
||||
}
|
||||
}
|
||||
|
||||
// Vérifier le manifest
|
||||
function checkManifest(manifestPath) {
|
||||
try {
|
||||
const content = fs.readFileSync(manifestPath, 'utf8')
|
||||
const manifest = JSON.parse(content)
|
||||
|
||||
console.log('✓ Manifest.json valide')
|
||||
console.log(` Version: ${manifest.version}`)
|
||||
console.log(` Permissions: ${manifest.permissions.join(', ')}`)
|
||||
console.log(` Host permissions: ${manifest.host_permissions.length}`)
|
||||
|
||||
} catch (error) {
|
||||
issues.push(`Manifest.json invalide: ${error.message}`)
|
||||
}
|
||||
}
|
||||
|
||||
// Exécuter les tests
|
||||
console.log('📋 Vérification des fichiers...\n')
|
||||
|
||||
checkSyntax(path.join(extDir, 'sidepanel.js'))
|
||||
checkSyntax(path.join(extDir, 'content.js'))
|
||||
checkSyntax(path.join(extDir, 'background.js'))
|
||||
|
||||
console.log('\n🔒 Vérification CSP...\n')
|
||||
checkInlineHandlers(path.join(extDir, 'sidepanel.html'))
|
||||
checkCSPFixes(path.join(extDir, 'sidepanel.js'))
|
||||
|
||||
console.log('\n🎯 Vérification fix pick mode...\n')
|
||||
checkPickModeFix(path.join(extDir, 'sidepanel.js'))
|
||||
|
||||
console.log('\n📦 Vérification manifest...\n')
|
||||
checkManifest(path.join(extDir, 'manifest.json'))
|
||||
|
||||
// Résumé
|
||||
console.log('\n' + '='.repeat(50))
|
||||
if (issues.length === 0 && warnings.length === 0) {
|
||||
console.log('✅ Aucun problème détecté !')
|
||||
} else {
|
||||
if (issues.length > 0) {
|
||||
console.log('\n❌ Problèmes détectés:')
|
||||
issues.forEach(issue => console.log(` • ${issue}`))
|
||||
}
|
||||
if (warnings.length > 0) {
|
||||
console.log('\n⚠️ Warnings:')
|
||||
warnings.forEach(warning => console.log(` • ${warning}`))
|
||||
}
|
||||
}
|
||||
|
||||
console.log('\n📝 Instructions:')
|
||||
console.log('1. Rechargez l\'extension dans chrome://extensions (bouton 🔄)')
|
||||
console.log('2. Ouvrez une page web normale')
|
||||
console.log('3. Cliquez sur l\'icône Memento')
|
||||
console.log('4. Fermez le sidepanel - la bannière doit disparaître')
|
||||
console.log('5. Ouvrez la console (F12) - pas d\'erreur CSP')
|
||||
@@ -1,215 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
/* eslint-disable @typescript-eslint/no-require-imports */
|
||||
'use strict'
|
||||
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
const zlib = require('zlib')
|
||||
|
||||
const VERSION = "0.3.1"
|
||||
|
||||
// Embedded Momento clipper/extension string table (gzip+base64).
|
||||
// Source English + contextual French anchors; other locales are MT batch
|
||||
// post-edited with placeholder hardening.
|
||||
|
||||
const PACK_B64 = `
|
||||
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
|
||||
wOW7jPkAAA==
|
||||
`
|
||||
|
||||
function loadStrings () {
|
||||
const buf = Buffer.from(PACK_B64.replace(/\s+/g, ''), 'base64')
|
||||
return JSON.parse(zlib.gunzipSync(buf).toString('utf8'))
|
||||
}
|
||||
|
||||
function main () {
|
||||
const STRINGS = loadStrings()
|
||||
const payload = {
|
||||
version: VERSION,
|
||||
strings: STRINGS,
|
||||
}
|
||||
fs.writeFileSync(
|
||||
path.join(__dirname, 'translations.json'),
|
||||
JSON.stringify(payload, null, 2) + "\n",
|
||||
'utf8'
|
||||
)
|
||||
console.log('Wrote', path.join(__dirname, 'translations.json'))
|
||||
}
|
||||
|
||||
main()
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"manifest_version": 3,
|
||||
"name": "__MSG_extName__",
|
||||
"version": "0.3.1",
|
||||
"version": "0.4.0",
|
||||
"description": "__MSG_extDescription__",
|
||||
"default_locale": "en",
|
||||
"permissions": [
|
||||
|
||||
Binary file not shown.
@@ -3,6 +3,12 @@ const ALLOW_INSTANCE_CONFIG = false
|
||||
const DEFAULT_BASE = 'https://memento-note.com'
|
||||
const STORAGE_KEYS = { baseUrl: 'memento_clipper_base_url', notebookId: 'memento_clipper_notebook_id' }
|
||||
|
||||
/** Compatibilité Chrome/Firefox : Firefox expose l'API sous `browser.*`,
|
||||
* Chrome sous `chrome.*`. On normalise pour utiliser `chrome.*` partout dans le code. */
|
||||
if (typeof chrome === 'undefined' && typeof browser !== 'undefined') {
|
||||
globalThis.chrome = browser
|
||||
}
|
||||
|
||||
let state = 'idle'
|
||||
let notebooks = []
|
||||
let selectedNotebookId = ''
|
||||
|
||||
@@ -1,78 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Test Sidepanel</title>
|
||||
<style>
|
||||
body { font-family: sans-serif; padding: 20px; }
|
||||
.error { color: red; }
|
||||
.success { color: green; }
|
||||
pre { background: #f5f5f5; padding: 10px; overflow-x: auto; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Test Extension Memento</h1>
|
||||
<div id="results"></div>
|
||||
|
||||
<script>
|
||||
const results = document.getElementById('results');
|
||||
|
||||
function log(message, type = 'info') {
|
||||
const div = document.createElement('div');
|
||||
div.className = type;
|
||||
div.textContent = message;
|
||||
results.appendChild(div);
|
||||
}
|
||||
|
||||
function testFile(filename) {
|
||||
return fetch(filename)
|
||||
.then(response => {
|
||||
if (!response.ok) throw new Error(`HTTP ${response.status}`);
|
||||
return response.text();
|
||||
})
|
||||
.then(content => {
|
||||
log(`✓ ${filename} chargé (${content.length} bytes)`, 'success');
|
||||
return { filename, content };
|
||||
})
|
||||
.catch(error => {
|
||||
log(`✗ ${filename}: ${error.message}`, 'error');
|
||||
return { filename, error };
|
||||
});
|
||||
}
|
||||
|
||||
async function testSidepanelJS() {
|
||||
const script = document.createElement('script');
|
||||
script.src = 'sidepanel.js';
|
||||
script.onerror = () => log('✗ sidepanel.js: Erreur de chargement', 'error');
|
||||
script.onload = () => {
|
||||
log('✓ sidepanel.js chargé', 'success');
|
||||
// Check for CSP violations
|
||||
if (typeof chrome !== 'undefined') {
|
||||
log('✓ API chrome disponible', 'success');
|
||||
} else {
|
||||
log('✗ API chrome non disponible (normal hors extension)', 'error');
|
||||
}
|
||||
};
|
||||
document.head.appendChild(script);
|
||||
}
|
||||
|
||||
async function runTests() {
|
||||
log('🧪 Début des tests...', 'info');
|
||||
|
||||
// Test file loading
|
||||
await Promise.all([
|
||||
testFile('sidepanel.html'),
|
||||
testFile('sidepanel.js'),
|
||||
testFile('content.js'),
|
||||
testFile('background.js'),
|
||||
testFile('manifest.json')
|
||||
]);
|
||||
|
||||
// Test sidepanel.js execution
|
||||
await testSidepanelJS();
|
||||
}
|
||||
|
||||
runTests();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
182
memento-note/extension/dist-firefox/_locales/ar/messages.json
Normal file
182
memento-note/extension/dist-firefox/_locales/ar/messages.json
Normal file
@@ -0,0 +1,182 @@
|
||||
{
|
||||
"extName": {
|
||||
"message": "مومنتو ويب كليبر"
|
||||
},
|
||||
"extDescription": {
|
||||
"message": "التقط صفحات الويب والنص المميز في دفاتر ملاحظات Memento الخاصة بك - ويتصل بخادم Memento الخاص بك."
|
||||
},
|
||||
"extActionTitle": {
|
||||
"message": "مقطع إلى مومنتو"
|
||||
},
|
||||
"webClipper": {
|
||||
"message": "مقص الويب"
|
||||
},
|
||||
"connected": {
|
||||
"message": "متصل"
|
||||
},
|
||||
"disconnected": {
|
||||
"message": "غير متصل"
|
||||
},
|
||||
"instanceSettings": {
|
||||
"message": "عنوان URL لمومنتو"
|
||||
},
|
||||
"instanceUrlLabel": {
|
||||
"message": "عنوان URL لمثيل Memento الخاص بك"
|
||||
},
|
||||
"presetProduction": {
|
||||
"message": "إعداد مسبق للإنتاج · memento-note.com"
|
||||
},
|
||||
"applyReconnect": {
|
||||
"message": "تطبيق وإعادة الاتصال"
|
||||
},
|
||||
"openMomento": {
|
||||
"message": "افتح مومنتو"
|
||||
},
|
||||
"settingsHint": {
|
||||
"message": "الصق عنوان URL الخاص بـ HTTPS (أو LAN) لخادم Memento الخاص بك. تتعامل ملفات تعريف الارتباط الموجودة في هذا المتصفح مع تسجيل الدخول."
|
||||
},
|
||||
"footerVersion": {
|
||||
"message": "Memento Web Clipper <<<الإصدار>>>"
|
||||
},
|
||||
"errPermissionDenied": {
|
||||
"message": "لا يستطيع Memento الوصول إلى علامة التبويب هذه. تحقق من أذونات ملحق لوحة المفاتيح/الموقع — أو افتح اللوحة الجانبية."
|
||||
},
|
||||
"notebookUnnamed": {
|
||||
"message": "دفتر بلا عنوان"
|
||||
},
|
||||
"noNotebooks": {
|
||||
"message": "لا توجد دفاتر ملاحظات حتى الآن"
|
||||
},
|
||||
"readingTimeOne": {
|
||||
"message": "~1 دقيقة قراءة"
|
||||
},
|
||||
"readingTimeOther": {
|
||||
"message": "تقريبا. $COUNT$ دقيقة قراءة",
|
||||
"placeholders": {
|
||||
"COUNT": {
|
||||
"content": "$1",
|
||||
"example": "5"
|
||||
}
|
||||
}
|
||||
},
|
||||
"selectionDetected": {
|
||||
"message": "تم الكشف عن التحديد"
|
||||
},
|
||||
"ignore": {
|
||||
"message": "يتجاهل"
|
||||
},
|
||||
"selectionHint": {
|
||||
"message": "نصيحة: قم بتمييز النص الموجود على الصفحة لقص التحديد الدقيق كملاحظة."
|
||||
},
|
||||
"clipSelection": {
|
||||
"message": "اختيار المقطع"
|
||||
},
|
||||
"clipPage": {
|
||||
"message": "قص هذه الصفحة"
|
||||
},
|
||||
"saveLinkOnly": {
|
||||
"message": "حفظ الرابط فقط"
|
||||
},
|
||||
"pageNotAccessible": {
|
||||
"message": "لا يمكن القص هنا — هذه الصفحة تحظر الوصول إلى الإضافات."
|
||||
},
|
||||
"errLoginRequired": {
|
||||
"message": "يرجى تسجيل الدخول إلى Memento في هذا المتصفح أولاً."
|
||||
},
|
||||
"errLoadNotebooks": {
|
||||
"message": "تعذر تحميل دفاتر الملاحظات. حاول إعادة الاتصال."
|
||||
},
|
||||
"notebooksLoaded": {
|
||||
"message": "تم تحميل دفاتر الملاحظات"
|
||||
},
|
||||
"connecting": {
|
||||
"message": "جارٍ الاتصال…"
|
||||
},
|
||||
"connectedToUrl": {
|
||||
"message": "متصل بـ $URL$",
|
||||
"placeholders": {
|
||||
"URL": {
|
||||
"content": "$1",
|
||||
"example": "https://memento-note.com"
|
||||
}
|
||||
}
|
||||
},
|
||||
"restrictedPage": {
|
||||
"message": "صفحة مقيدة - قم بالقص عبر شريط أدوات Memento أو اللوحة الجانبية."
|
||||
},
|
||||
"destinationNotebook": {
|
||||
"message": "دفتر الوجهة"
|
||||
},
|
||||
"activePage": {
|
||||
"message": "صفحة نشطة"
|
||||
},
|
||||
"previewBeforeSave": {
|
||||
"message": "المراجعة قبل الحفظ"
|
||||
},
|
||||
"noteTitleLabel": {
|
||||
"message": "عنوان"
|
||||
},
|
||||
"excerptLabel": {
|
||||
"message": "مقتطفات"
|
||||
},
|
||||
"saveToMomento": {
|
||||
"message": "حفظ إلى مومنتو"
|
||||
},
|
||||
"back": {
|
||||
"message": "خلف"
|
||||
},
|
||||
"analyzingSource": {
|
||||
"message": "تحليل المصدر"
|
||||
},
|
||||
"statusAnalyzing": {
|
||||
"message": "جارٍ التحليل…"
|
||||
},
|
||||
"statusSaving": {
|
||||
"message": "توفير…"
|
||||
},
|
||||
"processingDetail": {
|
||||
"message": "إنشاء العلامات والملخص الدلالي والتضمينات."
|
||||
},
|
||||
"noteSaved": {
|
||||
"message": "تم حفظ الملاحظة"
|
||||
},
|
||||
"sentToNotebook": {
|
||||
"message": "تم الحفظ في $NOTEBOOK$",
|
||||
"placeholders": {
|
||||
"NOTEBOOK": {
|
||||
"content": "$1",
|
||||
"example": "Read later"
|
||||
}
|
||||
}
|
||||
},
|
||||
"viewInMomento": {
|
||||
"message": "عرض في مومنتو"
|
||||
},
|
||||
"clipAnother": {
|
||||
"message": "قص صفحة أخرى"
|
||||
},
|
||||
"failure": {
|
||||
"message": "لا يمكن إكماله"
|
||||
},
|
||||
"genericError": {
|
||||
"message": "حدث خطأ ما أثناء الوصول إلى مثيل Memento."
|
||||
},
|
||||
"retry": {
|
||||
"message": "أعد المحاولة"
|
||||
},
|
||||
"errNoSelection": {
|
||||
"message": "حدد النص أولاً، أو قم بقص الصفحة بأكملها."
|
||||
},
|
||||
"errAnalyzeFailed": {
|
||||
"message": "لا يمكن تحليل هذه الصفحة."
|
||||
},
|
||||
"errSaveFailed": {
|
||||
"message": "لا يمكن حفظ ملاحظتك."
|
||||
},
|
||||
"errNetwork": {
|
||||
"message": "مشكلة في الشبكة - تحقق من اتصالك وعنوان URL الخاص بـ Memento."
|
||||
},
|
||||
"bannerPickText": {
|
||||
"message": "قم بتمييز النص الموجود على الصفحة، أو قم بقص الصفحة بأكملها."
|
||||
}
|
||||
}
|
||||
182
memento-note/extension/dist-firefox/_locales/de/messages.json
Normal file
182
memento-note/extension/dist-firefox/_locales/de/messages.json
Normal file
@@ -0,0 +1,182 @@
|
||||
{
|
||||
"extName": {
|
||||
"message": "Memento Web Clipper"
|
||||
},
|
||||
"extDescription": {
|
||||
"message": "Erfassen Sie Webseiten und hervorgehobenen Text in Ihren Memento-Notizbüchern – stellt eine Verbindung zu Ihrem eigenen Memento-Server her."
|
||||
},
|
||||
"extActionTitle": {
|
||||
"message": "Clip auf Memento"
|
||||
},
|
||||
"webClipper": {
|
||||
"message": "Web Clipper"
|
||||
},
|
||||
"connected": {
|
||||
"message": "Verbunden"
|
||||
},
|
||||
"disconnected": {
|
||||
"message": "Nicht verbunden"
|
||||
},
|
||||
"instanceSettings": {
|
||||
"message": "Memento-URL"
|
||||
},
|
||||
"instanceUrlLabel": {
|
||||
"message": "Ihre Memento-Instanz-URL"
|
||||
},
|
||||
"presetProduction": {
|
||||
"message": "Produktionsvoreinstellung · memento-note.com"
|
||||
},
|
||||
"applyReconnect": {
|
||||
"message": "Anwenden und erneut verbinden"
|
||||
},
|
||||
"openMomento": {
|
||||
"message": "Öffnen Sie Memento"
|
||||
},
|
||||
"settingsHint": {
|
||||
"message": "Fügen Sie die HTTPS- (oder LAN-)URL Ihres Memento-Servers ein. Cookies in diesem Browser verarbeiten die Anmeldung."
|
||||
},
|
||||
"footerVersion": {
|
||||
"message": "Memento Web Clipper 0.4.0"
|
||||
},
|
||||
"errPermissionDenied": {
|
||||
"message": "Memento kann nicht auf diese Registerkarte zugreifen. Überprüfen Sie die Tastatur-/Site-Erweiterungsberechtigungen – oder öffnen Sie den Seitenbereich."
|
||||
},
|
||||
"notebookUnnamed": {
|
||||
"message": "Notizbuch ohne Titel"
|
||||
},
|
||||
"noNotebooks": {
|
||||
"message": "Noch keine Notizbücher"
|
||||
},
|
||||
"readingTimeOne": {
|
||||
"message": "~1 Minute gelesen"
|
||||
},
|
||||
"readingTimeOther": {
|
||||
"message": "Ca. $COUNT$ min. gelesen",
|
||||
"placeholders": {
|
||||
"COUNT": {
|
||||
"content": "$1",
|
||||
"example": "5"
|
||||
}
|
||||
}
|
||||
},
|
||||
"selectionDetected": {
|
||||
"message": "Auswahl erkannt"
|
||||
},
|
||||
"ignore": {
|
||||
"message": "ignorieren"
|
||||
},
|
||||
"selectionHint": {
|
||||
"message": "Tipp: Markieren Sie Text auf der Seite, um eine präzise Auswahl als Notiz auszuschneiden."
|
||||
},
|
||||
"clipSelection": {
|
||||
"message": "Clip-Auswahl"
|
||||
},
|
||||
"clipPage": {
|
||||
"message": "Clip diese Seite aus"
|
||||
},
|
||||
"saveLinkOnly": {
|
||||
"message": "Nur Link speichern"
|
||||
},
|
||||
"pageNotAccessible": {
|
||||
"message": "Hier kann kein Clip erstellt werden – diese Seite blockiert den Zugriff auf die Erweiterung."
|
||||
},
|
||||
"errLoginRequired": {
|
||||
"message": "Bitte melden Sie sich zunächst in diesem Browser bei Memento an."
|
||||
},
|
||||
"errLoadNotebooks": {
|
||||
"message": "Notebooks konnten nicht geladen werden. Versuchen Sie, die Verbindung wiederherzustellen."
|
||||
},
|
||||
"notebooksLoaded": {
|
||||
"message": "Notizbücher geladen"
|
||||
},
|
||||
"connecting": {
|
||||
"message": "Verbinden…"
|
||||
},
|
||||
"connectedToUrl": {
|
||||
"message": "Verbunden mit $URL$",
|
||||
"placeholders": {
|
||||
"URL": {
|
||||
"content": "$1",
|
||||
"example": "https://memento-note.com"
|
||||
}
|
||||
}
|
||||
},
|
||||
"restrictedPage": {
|
||||
"message": "Eingeschränkte Seite – Ausschneiden über die Memento-Symbolleiste oder den Seitenbereich."
|
||||
},
|
||||
"destinationNotebook": {
|
||||
"message": "Zielnotizbuch"
|
||||
},
|
||||
"activePage": {
|
||||
"message": "Aktive Seite"
|
||||
},
|
||||
"previewBeforeSave": {
|
||||
"message": "Vor dem Speichern überprüfen"
|
||||
},
|
||||
"noteTitleLabel": {
|
||||
"message": "Titel"
|
||||
},
|
||||
"excerptLabel": {
|
||||
"message": "Auszug"
|
||||
},
|
||||
"saveToMomento": {
|
||||
"message": "In Memento speichern"
|
||||
},
|
||||
"back": {
|
||||
"message": "Zurück"
|
||||
},
|
||||
"analyzingSource": {
|
||||
"message": "Quelle analysieren"
|
||||
},
|
||||
"statusAnalyzing": {
|
||||
"message": "Analysieren…"
|
||||
},
|
||||
"statusSaving": {
|
||||
"message": "Sparen…"
|
||||
},
|
||||
"processingDetail": {
|
||||
"message": "Generieren von Tags, einer semantischen Zusammenfassung und Einbettungen."
|
||||
},
|
||||
"noteSaved": {
|
||||
"message": "Notiz gespeichert"
|
||||
},
|
||||
"sentToNotebook": {
|
||||
"message": "Gespeichert unter $NOTEBOOK$",
|
||||
"placeholders": {
|
||||
"NOTEBOOK": {
|
||||
"content": "$1",
|
||||
"example": "Read later"
|
||||
}
|
||||
}
|
||||
},
|
||||
"viewInMomento": {
|
||||
"message": "In Memento ansehen"
|
||||
},
|
||||
"clipAnother": {
|
||||
"message": "Schneiden Sie eine weitere Seite aus"
|
||||
},
|
||||
"failure": {
|
||||
"message": "Konnte nicht abgeschlossen werden"
|
||||
},
|
||||
"genericError": {
|
||||
"message": "Beim Erreichen Ihrer Memento-Instanz ist ein Fehler aufgetreten."
|
||||
},
|
||||
"retry": {
|
||||
"message": "Wiederholen"
|
||||
},
|
||||
"errNoSelection": {
|
||||
"message": "Wählen Sie zuerst den Text aus oder schneiden Sie die gesamte Seite aus."
|
||||
},
|
||||
"errAnalyzeFailed": {
|
||||
"message": "Diese Seite konnte nicht analysiert werden."
|
||||
},
|
||||
"errSaveFailed": {
|
||||
"message": "Ihre Notiz konnte nicht gespeichert werden."
|
||||
},
|
||||
"errNetwork": {
|
||||
"message": "Netzwerkproblem – überprüfen Sie Ihre Verbindung und Memento-URL."
|
||||
},
|
||||
"bannerPickText": {
|
||||
"message": "Markieren Sie Text auf der Seite oder schneiden Sie die gesamte Seite aus."
|
||||
}
|
||||
}
|
||||
182
memento-note/extension/dist-firefox/_locales/en/messages.json
Normal file
182
memento-note/extension/dist-firefox/_locales/en/messages.json
Normal file
@@ -0,0 +1,182 @@
|
||||
{
|
||||
"extName": {
|
||||
"message": "Memento Web Clipper"
|
||||
},
|
||||
"extDescription": {
|
||||
"message": "Capture web pages and highlighted text into your Memento notebooks — connects to your own Memento server."
|
||||
},
|
||||
"extActionTitle": {
|
||||
"message": "Clip to Memento"
|
||||
},
|
||||
"webClipper": {
|
||||
"message": "Web Clipper"
|
||||
},
|
||||
"connected": {
|
||||
"message": "Connected"
|
||||
},
|
||||
"disconnected": {
|
||||
"message": "Not connected"
|
||||
},
|
||||
"instanceSettings": {
|
||||
"message": "Memento URL"
|
||||
},
|
||||
"instanceUrlLabel": {
|
||||
"message": "Your Memento instance URL"
|
||||
},
|
||||
"presetProduction": {
|
||||
"message": "Production preset · memento-note.com"
|
||||
},
|
||||
"applyReconnect": {
|
||||
"message": "Apply and reconnect"
|
||||
},
|
||||
"openMomento": {
|
||||
"message": "Open Memento"
|
||||
},
|
||||
"settingsHint": {
|
||||
"message": "Paste the HTTPS (or LAN) URL of your Memento server. Cookies in this browser handle sign-in."
|
||||
},
|
||||
"footerVersion": {
|
||||
"message": "Memento Web Clipper 0.4.0"
|
||||
},
|
||||
"errPermissionDenied": {
|
||||
"message": "Memento can't access this tab. Check keyboard/site extension permissions — or open the Side Panel."
|
||||
},
|
||||
"notebookUnnamed": {
|
||||
"message": "Untitled notebook"
|
||||
},
|
||||
"noNotebooks": {
|
||||
"message": "No notebooks yet"
|
||||
},
|
||||
"readingTimeOne": {
|
||||
"message": "~1 minute read"
|
||||
},
|
||||
"readingTimeOther": {
|
||||
"message": "Approx. $COUNT$ min read",
|
||||
"placeholders": {
|
||||
"COUNT": {
|
||||
"content": "$1",
|
||||
"example": "5"
|
||||
}
|
||||
}
|
||||
},
|
||||
"selectionDetected": {
|
||||
"message": "Selection detected"
|
||||
},
|
||||
"ignore": {
|
||||
"message": "ignore"
|
||||
},
|
||||
"selectionHint": {
|
||||
"message": "Tip: highlight text on the page to clip a precise selection as a note."
|
||||
},
|
||||
"clipSelection": {
|
||||
"message": "Clip selection"
|
||||
},
|
||||
"clipPage": {
|
||||
"message": "Clip this page"
|
||||
},
|
||||
"saveLinkOnly": {
|
||||
"message": "Save link only"
|
||||
},
|
||||
"pageNotAccessible": {
|
||||
"message": "Can't clip here — this page blocks extension access."
|
||||
},
|
||||
"errLoginRequired": {
|
||||
"message": "Please sign in to Memento in this browser first."
|
||||
},
|
||||
"errLoadNotebooks": {
|
||||
"message": "Could not load notebooks. Try reconnecting."
|
||||
},
|
||||
"notebooksLoaded": {
|
||||
"message": "Notebooks loaded"
|
||||
},
|
||||
"connecting": {
|
||||
"message": "Connecting…"
|
||||
},
|
||||
"connectedToUrl": {
|
||||
"message": "Connected to $URL$",
|
||||
"placeholders": {
|
||||
"URL": {
|
||||
"content": "$1",
|
||||
"example": "https://memento-note.com"
|
||||
}
|
||||
}
|
||||
},
|
||||
"restrictedPage": {
|
||||
"message": "Restricted page — clip via the Memento toolbar or Side Panel."
|
||||
},
|
||||
"destinationNotebook": {
|
||||
"message": "Destination notebook"
|
||||
},
|
||||
"activePage": {
|
||||
"message": "Active page"
|
||||
},
|
||||
"previewBeforeSave": {
|
||||
"message": "Review before saving"
|
||||
},
|
||||
"noteTitleLabel": {
|
||||
"message": "Title"
|
||||
},
|
||||
"excerptLabel": {
|
||||
"message": "Excerpt"
|
||||
},
|
||||
"saveToMomento": {
|
||||
"message": "Save to Memento"
|
||||
},
|
||||
"back": {
|
||||
"message": "Back"
|
||||
},
|
||||
"analyzingSource": {
|
||||
"message": "Analyzing source"
|
||||
},
|
||||
"statusAnalyzing": {
|
||||
"message": "Analyzing…"
|
||||
},
|
||||
"statusSaving": {
|
||||
"message": "Saving…"
|
||||
},
|
||||
"processingDetail": {
|
||||
"message": "Generating tags, a semantic summary, and embeddings."
|
||||
},
|
||||
"noteSaved": {
|
||||
"message": "Note saved"
|
||||
},
|
||||
"sentToNotebook": {
|
||||
"message": "Saved to $NOTEBOOK$",
|
||||
"placeholders": {
|
||||
"NOTEBOOK": {
|
||||
"content": "$1",
|
||||
"example": "Read later"
|
||||
}
|
||||
}
|
||||
},
|
||||
"viewInMomento": {
|
||||
"message": "View in Memento"
|
||||
},
|
||||
"clipAnother": {
|
||||
"message": "Clip another page"
|
||||
},
|
||||
"failure": {
|
||||
"message": "Could not complete"
|
||||
},
|
||||
"genericError": {
|
||||
"message": "Something went wrong reaching your Memento instance."
|
||||
},
|
||||
"retry": {
|
||||
"message": "Retry"
|
||||
},
|
||||
"errNoSelection": {
|
||||
"message": "Select text first, or clip the full page."
|
||||
},
|
||||
"errAnalyzeFailed": {
|
||||
"message": "Could not analyze this page."
|
||||
},
|
||||
"errSaveFailed": {
|
||||
"message": "Could not save your note."
|
||||
},
|
||||
"errNetwork": {
|
||||
"message": "Network issue — check your connection and Memento URL."
|
||||
},
|
||||
"bannerPickText": {
|
||||
"message": "Highlight text on the page, or clip the whole page."
|
||||
}
|
||||
}
|
||||
182
memento-note/extension/dist-firefox/_locales/es/messages.json
Normal file
182
memento-note/extension/dist-firefox/_locales/es/messages.json
Normal file
@@ -0,0 +1,182 @@
|
||||
{
|
||||
"extName": {
|
||||
"message": "Cortadora web Memento"
|
||||
},
|
||||
"extDescription": {
|
||||
"message": "Capture páginas web y texto resaltado en sus cuadernos Memento: se conecta a su propio servidor Memento."
|
||||
},
|
||||
"extActionTitle": {
|
||||
"message": "Clip al momento"
|
||||
},
|
||||
"webClipper": {
|
||||
"message": "Cortadora web"
|
||||
},
|
||||
"connected": {
|
||||
"message": "Conectado"
|
||||
},
|
||||
"disconnected": {
|
||||
"message": "No conectado"
|
||||
},
|
||||
"instanceSettings": {
|
||||
"message": "URL del momento"
|
||||
},
|
||||
"instanceUrlLabel": {
|
||||
"message": "La URL de tu instancia de Memento"
|
||||
},
|
||||
"presetProduction": {
|
||||
"message": "Preajuste de producción · memento-note.com"
|
||||
},
|
||||
"applyReconnect": {
|
||||
"message": "Aplicar y reconectar"
|
||||
},
|
||||
"openMomento": {
|
||||
"message": "Memento abierto"
|
||||
},
|
||||
"settingsHint": {
|
||||
"message": "Pegue la URL HTTPS (o LAN) de su servidor Memento. Las cookies en este navegador controlan el inicio de sesión."
|
||||
},
|
||||
"footerVersion": {
|
||||
"message": "Memento Web Clipper <<<VERSIÓN>>>"
|
||||
},
|
||||
"errPermissionDenied": {
|
||||
"message": "Memento no puede acceder a esta pestaña. Verifique los permisos de extensión del sitio/teclado o abra el Panel lateral."
|
||||
},
|
||||
"notebookUnnamed": {
|
||||
"message": "Cuaderno sin título"
|
||||
},
|
||||
"noNotebooks": {
|
||||
"message": "Aún no hay cuadernos"
|
||||
},
|
||||
"readingTimeOne": {
|
||||
"message": "~1 minuto de lectura"
|
||||
},
|
||||
"readingTimeOther": {
|
||||
"message": "Aprox. <<<CONTAR>>> lectura mínima (Approx. $COUNT$ min read)",
|
||||
"placeholders": {
|
||||
"COUNT": {
|
||||
"content": "$1",
|
||||
"example": "5"
|
||||
}
|
||||
}
|
||||
},
|
||||
"selectionDetected": {
|
||||
"message": "Selección detectada"
|
||||
},
|
||||
"ignore": {
|
||||
"message": "ignorar"
|
||||
},
|
||||
"selectionHint": {
|
||||
"message": "Consejo: resalte el texto en la página para recortar una selección precisa como nota."
|
||||
},
|
||||
"clipSelection": {
|
||||
"message": "Selección de clips"
|
||||
},
|
||||
"clipPage": {
|
||||
"message": "Recortar esta página"
|
||||
},
|
||||
"saveLinkOnly": {
|
||||
"message": "Guardar enlace solamente"
|
||||
},
|
||||
"pageNotAccessible": {
|
||||
"message": "No se puede recortar aquí: esta página bloquea el acceso a la extensión."
|
||||
},
|
||||
"errLoginRequired": {
|
||||
"message": "Primero inicie sesión en Memento en este navegador."
|
||||
},
|
||||
"errLoadNotebooks": {
|
||||
"message": "No se pudieron cargar los cuadernos. Intente volver a conectarse."
|
||||
},
|
||||
"notebooksLoaded": {
|
||||
"message": "Cuadernos cargados"
|
||||
},
|
||||
"connecting": {
|
||||
"message": "Conectando…"
|
||||
},
|
||||
"connectedToUrl": {
|
||||
"message": "Conectado a $URL$",
|
||||
"placeholders": {
|
||||
"URL": {
|
||||
"content": "$1",
|
||||
"example": "https://memento-note.com"
|
||||
}
|
||||
}
|
||||
},
|
||||
"restrictedPage": {
|
||||
"message": "Página restringida: recorte mediante la barra de herramientas de Memento o el panel lateral."
|
||||
},
|
||||
"destinationNotebook": {
|
||||
"message": "Cuaderno de destino"
|
||||
},
|
||||
"activePage": {
|
||||
"message": "Página activa"
|
||||
},
|
||||
"previewBeforeSave": {
|
||||
"message": "Revisar antes de guardar"
|
||||
},
|
||||
"noteTitleLabel": {
|
||||
"message": "Título"
|
||||
},
|
||||
"excerptLabel": {
|
||||
"message": "Extracto"
|
||||
},
|
||||
"saveToMomento": {
|
||||
"message": "Guardar en momento"
|
||||
},
|
||||
"back": {
|
||||
"message": "Atrás"
|
||||
},
|
||||
"analyzingSource": {
|
||||
"message": "Analizando fuente"
|
||||
},
|
||||
"statusAnalyzing": {
|
||||
"message": "Analizando…"
|
||||
},
|
||||
"statusSaving": {
|
||||
"message": "Ahorro…"
|
||||
},
|
||||
"processingDetail": {
|
||||
"message": "Generación de etiquetas, resumen semántico e incrustaciones."
|
||||
},
|
||||
"noteSaved": {
|
||||
"message": "Nota guardada"
|
||||
},
|
||||
"sentToNotebook": {
|
||||
"message": "Guardado en $NOTEBOOK$",
|
||||
"placeholders": {
|
||||
"NOTEBOOK": {
|
||||
"content": "$1",
|
||||
"example": "Read later"
|
||||
}
|
||||
}
|
||||
},
|
||||
"viewInMomento": {
|
||||
"message": "Ver en momento"
|
||||
},
|
||||
"clipAnother": {
|
||||
"message": "Recortar otra página"
|
||||
},
|
||||
"failure": {
|
||||
"message": "No se pudo completar"
|
||||
},
|
||||
"genericError": {
|
||||
"message": "Algo salió mal al llegar a tu instancia de Memento."
|
||||
},
|
||||
"retry": {
|
||||
"message": "Rever"
|
||||
},
|
||||
"errNoSelection": {
|
||||
"message": "Seleccione el texto primero o recorte la página completa."
|
||||
},
|
||||
"errAnalyzeFailed": {
|
||||
"message": "No se pudo analizar esta página."
|
||||
},
|
||||
"errSaveFailed": {
|
||||
"message": "No se pudo guardar tu nota."
|
||||
},
|
||||
"errNetwork": {
|
||||
"message": "Problema de red: verifique su conexión y la URL de Memento."
|
||||
},
|
||||
"bannerPickText": {
|
||||
"message": "Resalte el texto de la página o recorte toda la página."
|
||||
}
|
||||
}
|
||||
182
memento-note/extension/dist-firefox/_locales/fa/messages.json
Normal file
182
memento-note/extension/dist-firefox/_locales/fa/messages.json
Normal file
@@ -0,0 +1,182 @@
|
||||
{
|
||||
"extName": {
|
||||
"message": "Memento Web Clipper"
|
||||
},
|
||||
"extDescription": {
|
||||
"message": "صفحات وب و متن هایلایت شده را در نوت بوک های Memento خود ضبط کنید — به سرور Memento خودتان متصل می شود."
|
||||
},
|
||||
"extActionTitle": {
|
||||
"message": "کلیپ به لحظه"
|
||||
},
|
||||
"webClipper": {
|
||||
"message": "Web Clipper"
|
||||
},
|
||||
"connected": {
|
||||
"message": "متصل شد"
|
||||
},
|
||||
"disconnected": {
|
||||
"message": "متصل نیست"
|
||||
},
|
||||
"instanceSettings": {
|
||||
"message": "آدرس لحظه ای"
|
||||
},
|
||||
"instanceUrlLabel": {
|
||||
"message": "URL نمونه Memento شما"
|
||||
},
|
||||
"presetProduction": {
|
||||
"message": "پیش تنظیم تولید · memento-note.com"
|
||||
},
|
||||
"applyReconnect": {
|
||||
"message": "درخواست کنید و دوباره وصل شوید"
|
||||
},
|
||||
"openMomento": {
|
||||
"message": "Memento را باز کنید"
|
||||
},
|
||||
"settingsHint": {
|
||||
"message": "URL HTTPS (یا LAN) سرور Memento خود را جایگذاری کنید. کوکیهای این مرورگر ورود به سیستم را کنترل میکنند."
|
||||
},
|
||||
"footerVersion": {
|
||||
"message": "Memento Web Clipper 0.4.0"
|
||||
},
|
||||
"errPermissionDenied": {
|
||||
"message": "Memento نمی تواند به این برگه دسترسی پیدا کند. مجوزهای افزونه صفحه کلید/سایت را بررسی کنید - یا پانل جانبی را باز کنید."
|
||||
},
|
||||
"notebookUnnamed": {
|
||||
"message": "دفترچه بدون عنوان"
|
||||
},
|
||||
"noNotebooks": {
|
||||
"message": "هنوز نوت بوک نیست"
|
||||
},
|
||||
"readingTimeOne": {
|
||||
"message": "~ 1 دقیقه مطالعه کنید"
|
||||
},
|
||||
"readingTimeOther": {
|
||||
"message": "تقریبا $COUNT$ دقیقه خواندن",
|
||||
"placeholders": {
|
||||
"COUNT": {
|
||||
"content": "$1",
|
||||
"example": "5"
|
||||
}
|
||||
}
|
||||
},
|
||||
"selectionDetected": {
|
||||
"message": "انتخاب شناسایی شد"
|
||||
},
|
||||
"ignore": {
|
||||
"message": "نادیده گرفتن"
|
||||
},
|
||||
"selectionHint": {
|
||||
"message": "نکته: متن را در صفحه برجسته کنید تا یک انتخاب دقیق به عنوان یادداشت بریده شود."
|
||||
},
|
||||
"clipSelection": {
|
||||
"message": "انتخاب کلیپ"
|
||||
},
|
||||
"clipPage": {
|
||||
"message": "این صفحه را کلیپ کنید"
|
||||
},
|
||||
"saveLinkOnly": {
|
||||
"message": "فقط لینک را ذخیره کنید"
|
||||
},
|
||||
"pageNotAccessible": {
|
||||
"message": "در اینجا نمی توان کلیپ کرد - این صفحه دسترسی برنامه های افزودنی را مسدود می کند."
|
||||
},
|
||||
"errLoginRequired": {
|
||||
"message": "لطفاً ابتدا با این مرورگر وارد Memento شوید."
|
||||
},
|
||||
"errLoadNotebooks": {
|
||||
"message": "نوتبوکها بارگیری نشد. سعی کنید دوباره وصل شوید."
|
||||
},
|
||||
"notebooksLoaded": {
|
||||
"message": "نوت بوک ها بارگیری شدند"
|
||||
},
|
||||
"connecting": {
|
||||
"message": "در حال اتصال…"
|
||||
},
|
||||
"connectedToUrl": {
|
||||
"message": "به $URL$ متصل شد",
|
||||
"placeholders": {
|
||||
"URL": {
|
||||
"content": "$1",
|
||||
"example": "https://memento-note.com"
|
||||
}
|
||||
}
|
||||
},
|
||||
"restrictedPage": {
|
||||
"message": "صفحه محدود - از طریق نوار ابزار Memento یا پانل جانبی کلیپ کنید."
|
||||
},
|
||||
"destinationNotebook": {
|
||||
"message": "دفترچه یادداشت مقصد"
|
||||
},
|
||||
"activePage": {
|
||||
"message": "صفحه فعال"
|
||||
},
|
||||
"previewBeforeSave": {
|
||||
"message": "قبل از ذخیره بررسی کنید"
|
||||
},
|
||||
"noteTitleLabel": {
|
||||
"message": "عنوان"
|
||||
},
|
||||
"excerptLabel": {
|
||||
"message": "گزیده"
|
||||
},
|
||||
"saveToMomento": {
|
||||
"message": "ذخیره در Memento"
|
||||
},
|
||||
"back": {
|
||||
"message": "برگشت"
|
||||
},
|
||||
"analyzingSource": {
|
||||
"message": "تجزیه و تحلیل منبع"
|
||||
},
|
||||
"statusAnalyzing": {
|
||||
"message": "در حال تجزیه و تحلیل…"
|
||||
},
|
||||
"statusSaving": {
|
||||
"message": "در حال ذخیره…"
|
||||
},
|
||||
"processingDetail": {
|
||||
"message": "تولید برچسب ها، خلاصه معنایی، و جاسازی ها."
|
||||
},
|
||||
"noteSaved": {
|
||||
"message": "یادداشت ذخیره شد"
|
||||
},
|
||||
"sentToNotebook": {
|
||||
"message": "در $NOTEBOOK$ ذخیره شد",
|
||||
"placeholders": {
|
||||
"NOTEBOOK": {
|
||||
"content": "$1",
|
||||
"example": "Read later"
|
||||
}
|
||||
}
|
||||
},
|
||||
"viewInMomento": {
|
||||
"message": "مشاهده در Memento"
|
||||
},
|
||||
"clipAnother": {
|
||||
"message": "یک صفحه دیگر را کلیپ کنید"
|
||||
},
|
||||
"failure": {
|
||||
"message": "تکمیل نشد"
|
||||
},
|
||||
"genericError": {
|
||||
"message": "هنگام رسیدن به نمونه Memento شما مشکلی پیش آمد."
|
||||
},
|
||||
"retry": {
|
||||
"message": "دوباره امتحان کنید"
|
||||
},
|
||||
"errNoSelection": {
|
||||
"message": "ابتدا متن را انتخاب کنید، یا صفحه کامل را کلیپ کنید."
|
||||
},
|
||||
"errAnalyzeFailed": {
|
||||
"message": "نمی توان این صفحه را تجزیه و تحلیل کرد."
|
||||
},
|
||||
"errSaveFailed": {
|
||||
"message": "یادداشت شما ذخیره نشد."
|
||||
},
|
||||
"errNetwork": {
|
||||
"message": "مشکل شبکه - اتصال و URL Memento خود را بررسی کنید."
|
||||
},
|
||||
"bannerPickText": {
|
||||
"message": "متن را در صفحه برجسته کنید یا کل صفحه را برش دهید."
|
||||
}
|
||||
}
|
||||
182
memento-note/extension/dist-firefox/_locales/fr/messages.json
Normal file
182
memento-note/extension/dist-firefox/_locales/fr/messages.json
Normal file
@@ -0,0 +1,182 @@
|
||||
{
|
||||
"extName": {
|
||||
"message": "Memento · Web Clipper"
|
||||
},
|
||||
"extDescription": {
|
||||
"message": "Enregistrez des pages web et du texte surligné dans vos carnets Memento — connecté à votre propre serveur Memento."
|
||||
},
|
||||
"extActionTitle": {
|
||||
"message": "Clipper vers Memento"
|
||||
},
|
||||
"webClipper": {
|
||||
"message": "Tondeuse Web"
|
||||
},
|
||||
"connected": {
|
||||
"message": "Connecté"
|
||||
},
|
||||
"disconnected": {
|
||||
"message": "Non connecté"
|
||||
},
|
||||
"instanceSettings": {
|
||||
"message": "Adresse Memento"
|
||||
},
|
||||
"instanceUrlLabel": {
|
||||
"message": "URL de votre instance Memento"
|
||||
},
|
||||
"presetProduction": {
|
||||
"message": "Préréglage production · memento-note.com"
|
||||
},
|
||||
"applyReconnect": {
|
||||
"message": "Appliquer et reconnecter"
|
||||
},
|
||||
"openMomento": {
|
||||
"message": "Ouvrir Memento"
|
||||
},
|
||||
"settingsHint": {
|
||||
"message": "Collez l'URL HTTPS (ou LAN) de votre serveur Memento. Les cookies de ce navigateur gèrent la connexion."
|
||||
},
|
||||
"footerVersion": {
|
||||
"message": "Memento Web Clipper 0.4.0"
|
||||
},
|
||||
"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."
|
||||
},
|
||||
"notebookUnnamed": {
|
||||
"message": "Carnet sans titre"
|
||||
},
|
||||
"noNotebooks": {
|
||||
"message": "Pas encore de carnets"
|
||||
},
|
||||
"readingTimeOne": {
|
||||
"message": "≈ 1 minute de lecture"
|
||||
},
|
||||
"readingTimeOther": {
|
||||
"message": "≈ $COUNT$ minutes de lecture",
|
||||
"placeholders": {
|
||||
"COUNT": {
|
||||
"content": "$1",
|
||||
"example": "5"
|
||||
}
|
||||
}
|
||||
},
|
||||
"selectionDetected": {
|
||||
"message": "Sélection détectée"
|
||||
},
|
||||
"ignore": {
|
||||
"message": "ignorer"
|
||||
},
|
||||
"selectionHint": {
|
||||
"message": "Astuce : surlignez du texte à l’écran pour clipper une sélection précise de la page en tant que note."
|
||||
},
|
||||
"clipSelection": {
|
||||
"message": "Clipper la sélection"
|
||||
},
|
||||
"clipPage": {
|
||||
"message": "Clipper cette page"
|
||||
},
|
||||
"saveLinkOnly": {
|
||||
"message": "Enregistrer le lien uniquement"
|
||||
},
|
||||
"pageNotAccessible": {
|
||||
"message": "Impossible de découper ici : cette page bloque l'accès aux extensions."
|
||||
},
|
||||
"errLoginRequired": {
|
||||
"message": "Veuillez d'abord vous connecter à Memento dans ce navigateur."
|
||||
},
|
||||
"errLoadNotebooks": {
|
||||
"message": "Impossible de charger les blocs-notes. Essayez de vous reconnecter."
|
||||
},
|
||||
"notebooksLoaded": {
|
||||
"message": "Carnets chargés"
|
||||
},
|
||||
"connecting": {
|
||||
"message": "Connexion…"
|
||||
},
|
||||
"connectedToUrl": {
|
||||
"message": "Connecté à $URL$",
|
||||
"placeholders": {
|
||||
"URL": {
|
||||
"content": "$1",
|
||||
"example": "https://memento-note.com"
|
||||
}
|
||||
}
|
||||
},
|
||||
"restrictedPage": {
|
||||
"message": "Page restreinte : clip via la barre d'outils Memento ou le panneau latéral."
|
||||
},
|
||||
"destinationNotebook": {
|
||||
"message": "Carnet de destination"
|
||||
},
|
||||
"activePage": {
|
||||
"message": "Page active"
|
||||
},
|
||||
"previewBeforeSave": {
|
||||
"message": "Vérifier avant d'enregistrer"
|
||||
},
|
||||
"noteTitleLabel": {
|
||||
"message": "Titre"
|
||||
},
|
||||
"excerptLabel": {
|
||||
"message": "Extrait"
|
||||
},
|
||||
"saveToMomento": {
|
||||
"message": "Enregistrer dans Memento"
|
||||
},
|
||||
"back": {
|
||||
"message": "Dos"
|
||||
},
|
||||
"analyzingSource": {
|
||||
"message": "Analyse de la source"
|
||||
},
|
||||
"statusAnalyzing": {
|
||||
"message": "Analyser…"
|
||||
},
|
||||
"statusSaving": {
|
||||
"message": "Économie…"
|
||||
},
|
||||
"processingDetail": {
|
||||
"message": "Génération automatique des tags, résumé sémantique et calcul des embeddings."
|
||||
},
|
||||
"noteSaved": {
|
||||
"message": "Remarque enregistrée"
|
||||
},
|
||||
"sentToNotebook": {
|
||||
"message": "Enregistré dans $NOTEBOOK$",
|
||||
"placeholders": {
|
||||
"NOTEBOOK": {
|
||||
"content": "$1",
|
||||
"example": "Read later"
|
||||
}
|
||||
}
|
||||
},
|
||||
"viewInMomento": {
|
||||
"message": "Voir dans Memento"
|
||||
},
|
||||
"clipAnother": {
|
||||
"message": "Clipper une autre page"
|
||||
},
|
||||
"failure": {
|
||||
"message": "Impossible de terminer"
|
||||
},
|
||||
"genericError": {
|
||||
"message": "Une erreur est survenue lors de la communication avec votre instance Memento."
|
||||
},
|
||||
"retry": {
|
||||
"message": "Réessayer"
|
||||
},
|
||||
"errNoSelection": {
|
||||
"message": "Sélectionnez d’abord le texte ou coupez la page entière."
|
||||
},
|
||||
"errAnalyzeFailed": {
|
||||
"message": "Impossible d'analyser cette page."
|
||||
},
|
||||
"errSaveFailed": {
|
||||
"message": "Impossible d'enregistrer votre note."
|
||||
},
|
||||
"errNetwork": {
|
||||
"message": "Problème de réseau : vérifiez votre connexion et l'URL Memento."
|
||||
},
|
||||
"bannerPickText": {
|
||||
"message": "Mettez en surbrillance le texte sur la page ou coupez la page entière."
|
||||
}
|
||||
}
|
||||
182
memento-note/extension/dist-firefox/_locales/hi/messages.json
Normal file
182
memento-note/extension/dist-firefox/_locales/hi/messages.json
Normal file
@@ -0,0 +1,182 @@
|
||||
{
|
||||
"extName": {
|
||||
"message": "मोमेंटो वेब क्लिपर"
|
||||
},
|
||||
"extDescription": {
|
||||
"message": "अपने मोमेंटो नोटबुक में वेब पेज और हाइलाइट किए गए टेक्स्ट को कैप्चर करें - यह आपके अपने मोमेंटो सर्वर से जुड़ता है।"
|
||||
},
|
||||
"extActionTitle": {
|
||||
"message": "मोमेंटो पर क्लिप करें"
|
||||
},
|
||||
"webClipper": {
|
||||
"message": "वेब क्लिपर"
|
||||
},
|
||||
"connected": {
|
||||
"message": "जुड़े हुए"
|
||||
},
|
||||
"disconnected": {
|
||||
"message": "जुड़े नहीं हैं"
|
||||
},
|
||||
"instanceSettings": {
|
||||
"message": "मोमेंटो यूआरएल"
|
||||
},
|
||||
"instanceUrlLabel": {
|
||||
"message": "आपका मोमेंटो इंस्टेंस यूआरएल"
|
||||
},
|
||||
"presetProduction": {
|
||||
"message": "प्रोडक्शन प्रीसेट · memento-note.com"
|
||||
},
|
||||
"applyReconnect": {
|
||||
"message": "आवेदन करें और पुनः कनेक्ट करें"
|
||||
},
|
||||
"openMomento": {
|
||||
"message": "मोमेंटो खोलें"
|
||||
},
|
||||
"settingsHint": {
|
||||
"message": "अपने मोमेंटो सर्वर का HTTPS (या LAN) URL चिपकाएँ। इस ब्राउज़र में कुकीज़ साइन-इन को संभालती हैं।"
|
||||
},
|
||||
"footerVersion": {
|
||||
"message": "मोमेंटो वेब क्लिपर <<<संस्करण>>>"
|
||||
},
|
||||
"errPermissionDenied": {
|
||||
"message": "मोमेंटो इस टैब तक नहीं पहुंच सकता. कीबोर्ड/साइट एक्सटेंशन अनुमतियां जांचें - या साइड पैनल खोलें।"
|
||||
},
|
||||
"notebookUnnamed": {
|
||||
"message": "शीर्षक रहित नोटबुक"
|
||||
},
|
||||
"noNotebooks": {
|
||||
"message": "अभी तक कोई नोटबुक नहीं"
|
||||
},
|
||||
"readingTimeOne": {
|
||||
"message": "~1 मिनट पढ़ें"
|
||||
},
|
||||
"readingTimeOther": {
|
||||
"message": "लगभग। $COUNT$ मिनट पढ़ा",
|
||||
"placeholders": {
|
||||
"COUNT": {
|
||||
"content": "$1",
|
||||
"example": "5"
|
||||
}
|
||||
}
|
||||
},
|
||||
"selectionDetected": {
|
||||
"message": "चयन का पता चला"
|
||||
},
|
||||
"ignore": {
|
||||
"message": "अनदेखा करना"
|
||||
},
|
||||
"selectionHint": {
|
||||
"message": "युक्ति: किसी सटीक चयन को नोट के रूप में क्लिप करने के लिए पृष्ठ पर टेक्स्ट को हाइलाइट करें।"
|
||||
},
|
||||
"clipSelection": {
|
||||
"message": "क्लिप चयन"
|
||||
},
|
||||
"clipPage": {
|
||||
"message": "इस पृष्ठ को क्लिप करें"
|
||||
},
|
||||
"saveLinkOnly": {
|
||||
"message": "केवल लिंक सहेजें"
|
||||
},
|
||||
"pageNotAccessible": {
|
||||
"message": "यहां क्लिप नहीं किया जा सकता - यह पेज एक्सटेंशन एक्सेस को ब्लॉक करता है।"
|
||||
},
|
||||
"errLoginRequired": {
|
||||
"message": "कृपया पहले इस ब्राउज़र में मोमेंटो में साइन इन करें।"
|
||||
},
|
||||
"errLoadNotebooks": {
|
||||
"message": "नोटबुक लोड नहीं हो सकीं. पुनः कनेक्ट करने का प्रयास करें."
|
||||
},
|
||||
"notebooksLoaded": {
|
||||
"message": "नोटबुक लोड किए गए"
|
||||
},
|
||||
"connecting": {
|
||||
"message": "कनेक्ट हो रहा है..."
|
||||
},
|
||||
"connectedToUrl": {
|
||||
"message": "$URL$ से कनेक्ट किया गया",
|
||||
"placeholders": {
|
||||
"URL": {
|
||||
"content": "$1",
|
||||
"example": "https://memento-note.com"
|
||||
}
|
||||
}
|
||||
},
|
||||
"restrictedPage": {
|
||||
"message": "प्रतिबंधित पृष्ठ - मोमेंटो टूलबार या साइड पैनल के माध्यम से क्लिप करें।"
|
||||
},
|
||||
"destinationNotebook": {
|
||||
"message": "गंतव्य नोटबुक"
|
||||
},
|
||||
"activePage": {
|
||||
"message": "सक्रिय पृष्ठ"
|
||||
},
|
||||
"previewBeforeSave": {
|
||||
"message": "सहेजने से पहले समीक्षा करें"
|
||||
},
|
||||
"noteTitleLabel": {
|
||||
"message": "शीर्षक"
|
||||
},
|
||||
"excerptLabel": {
|
||||
"message": "अंश"
|
||||
},
|
||||
"saveToMomento": {
|
||||
"message": "मोमेंटो में सहेजें"
|
||||
},
|
||||
"back": {
|
||||
"message": "पीछे"
|
||||
},
|
||||
"analyzingSource": {
|
||||
"message": "स्रोत का विश्लेषण"
|
||||
},
|
||||
"statusAnalyzing": {
|
||||
"message": "विश्लेषण कर रहा हूँ..."
|
||||
},
|
||||
"statusSaving": {
|
||||
"message": "सहेजा जा रहा है..."
|
||||
},
|
||||
"processingDetail": {
|
||||
"message": "टैग, सिमेंटिक सारांश और एम्बेडिंग तैयार करना।"
|
||||
},
|
||||
"noteSaved": {
|
||||
"message": "नोट सहेजा गया"
|
||||
},
|
||||
"sentToNotebook": {
|
||||
"message": "$NOTEBOOK$ में सहेजा गया",
|
||||
"placeholders": {
|
||||
"NOTEBOOK": {
|
||||
"content": "$1",
|
||||
"example": "Read later"
|
||||
}
|
||||
}
|
||||
},
|
||||
"viewInMomento": {
|
||||
"message": "मोमेंटो में देखें"
|
||||
},
|
||||
"clipAnother": {
|
||||
"message": "दूसरे पेज को क्लिप करें"
|
||||
},
|
||||
"failure": {
|
||||
"message": "पूरा नहीं हो सका"
|
||||
},
|
||||
"genericError": {
|
||||
"message": "आपके मोमेंटो इंस्टेंस तक पहुँचने में कुछ गड़बड़ी हुई।"
|
||||
},
|
||||
"retry": {
|
||||
"message": "पुन: प्रयास करें"
|
||||
},
|
||||
"errNoSelection": {
|
||||
"message": "पहले टेक्स्ट चुनें, या पूरा पेज क्लिप करें।"
|
||||
},
|
||||
"errAnalyzeFailed": {
|
||||
"message": "इस पृष्ठ का विश्लेषण नहीं किया जा सका."
|
||||
},
|
||||
"errSaveFailed": {
|
||||
"message": "आपका नोट सहेजा नहीं जा सका."
|
||||
},
|
||||
"errNetwork": {
|
||||
"message": "नेटवर्क समस्या - अपना कनेक्शन और मोमेंटो यूआरएल जांचें।"
|
||||
},
|
||||
"bannerPickText": {
|
||||
"message": "पृष्ठ पर टेक्स्ट को हाइलाइट करें, या पूरे पृष्ठ को क्लिप करें।"
|
||||
}
|
||||
}
|
||||
182
memento-note/extension/dist-firefox/_locales/it/messages.json
Normal file
182
memento-note/extension/dist-firefox/_locales/it/messages.json
Normal file
@@ -0,0 +1,182 @@
|
||||
{
|
||||
"extName": {
|
||||
"message": "Memento Web Clipper"
|
||||
},
|
||||
"extDescription": {
|
||||
"message": "Cattura pagine web e testo evidenziato nei tuoi taccuini Memento: si connette al tuo server Memento."
|
||||
},
|
||||
"extActionTitle": {
|
||||
"message": "Clip su Memento"
|
||||
},
|
||||
"webClipper": {
|
||||
"message": "Tagliatore di fotoricettore"
|
||||
},
|
||||
"connected": {
|
||||
"message": "Collegato"
|
||||
},
|
||||
"disconnected": {
|
||||
"message": "Non connesso"
|
||||
},
|
||||
"instanceSettings": {
|
||||
"message": "URL del momento"
|
||||
},
|
||||
"instanceUrlLabel": {
|
||||
"message": "L'URL dell'istanza di Memento"
|
||||
},
|
||||
"presetProduction": {
|
||||
"message": "Preimpostazione di produzione · memento-note.com"
|
||||
},
|
||||
"applyReconnect": {
|
||||
"message": "Applicare e riconnettersi"
|
||||
},
|
||||
"openMomento": {
|
||||
"message": "Memento aperto"
|
||||
},
|
||||
"settingsHint": {
|
||||
"message": "Incolla l'URL HTTPS (o LAN) del tuo server Memento. I cookie in questo browser gestiscono l'accesso."
|
||||
},
|
||||
"footerVersion": {
|
||||
"message": "Memento Web Clipper <<<VERSIONE>>>"
|
||||
},
|
||||
"errPermissionDenied": {
|
||||
"message": "Memento non può accedere a questa scheda. Controlla le autorizzazioni per tastiera/estensione del sito oppure apri il pannello laterale."
|
||||
},
|
||||
"notebookUnnamed": {
|
||||
"message": "Taccuino senza titolo"
|
||||
},
|
||||
"noNotebooks": {
|
||||
"message": "Nessun taccuino ancora"
|
||||
},
|
||||
"readingTimeOne": {
|
||||
"message": "~1 minuto di lettura"
|
||||
},
|
||||
"readingTimeOther": {
|
||||
"message": "ca. $COUNT$ min letto",
|
||||
"placeholders": {
|
||||
"COUNT": {
|
||||
"content": "$1",
|
||||
"example": "5"
|
||||
}
|
||||
}
|
||||
},
|
||||
"selectionDetected": {
|
||||
"message": "Selezione rilevata"
|
||||
},
|
||||
"ignore": {
|
||||
"message": "ignorare"
|
||||
},
|
||||
"selectionHint": {
|
||||
"message": "Suggerimento: evidenzia il testo sulla pagina per ritagliare una selezione precisa come nota."
|
||||
},
|
||||
"clipSelection": {
|
||||
"message": "Selezione clip"
|
||||
},
|
||||
"clipPage": {
|
||||
"message": "Ritaglia questa pagina"
|
||||
},
|
||||
"saveLinkOnly": {
|
||||
"message": "Salva solo il collegamento"
|
||||
},
|
||||
"pageNotAccessible": {
|
||||
"message": "Impossibile ritagliare qui: questa pagina blocca l'accesso all'estensione."
|
||||
},
|
||||
"errLoginRequired": {
|
||||
"message": "Accedi prima a Memento in questo browser."
|
||||
},
|
||||
"errLoadNotebooks": {
|
||||
"message": "Impossibile caricare i taccuini. Prova a riconnetterti."
|
||||
},
|
||||
"notebooksLoaded": {
|
||||
"message": "Taccuini caricati"
|
||||
},
|
||||
"connecting": {
|
||||
"message": "Connessione…"
|
||||
},
|
||||
"connectedToUrl": {
|
||||
"message": "Connesso a $URL$",
|
||||
"placeholders": {
|
||||
"URL": {
|
||||
"content": "$1",
|
||||
"example": "https://memento-note.com"
|
||||
}
|
||||
}
|
||||
},
|
||||
"restrictedPage": {
|
||||
"message": "Pagina limitata: ritaglia tramite la barra degli strumenti Memento o il pannello laterale."
|
||||
},
|
||||
"destinationNotebook": {
|
||||
"message": "Taccuino di destinazione"
|
||||
},
|
||||
"activePage": {
|
||||
"message": "Pagina attiva"
|
||||
},
|
||||
"previewBeforeSave": {
|
||||
"message": "Rivedi prima di salvare"
|
||||
},
|
||||
"noteTitleLabel": {
|
||||
"message": "Titolo"
|
||||
},
|
||||
"excerptLabel": {
|
||||
"message": "Estratto"
|
||||
},
|
||||
"saveToMomento": {
|
||||
"message": "Salva su Memento"
|
||||
},
|
||||
"back": {
|
||||
"message": "Indietro"
|
||||
},
|
||||
"analyzingSource": {
|
||||
"message": "Analisi della fonte"
|
||||
},
|
||||
"statusAnalyzing": {
|
||||
"message": "Analizzando..."
|
||||
},
|
||||
"statusSaving": {
|
||||
"message": "Risparmio…"
|
||||
},
|
||||
"processingDetail": {
|
||||
"message": "Generazione di tag, riepilogo semantico e incorporamenti."
|
||||
},
|
||||
"noteSaved": {
|
||||
"message": "Nota salvata"
|
||||
},
|
||||
"sentToNotebook": {
|
||||
"message": "Salvato in $NOTEBOOK$",
|
||||
"placeholders": {
|
||||
"NOTEBOOK": {
|
||||
"content": "$1",
|
||||
"example": "Read later"
|
||||
}
|
||||
}
|
||||
},
|
||||
"viewInMomento": {
|
||||
"message": "Visualizza in Memento"
|
||||
},
|
||||
"clipAnother": {
|
||||
"message": "Ritaglia un'altra pagina"
|
||||
},
|
||||
"failure": {
|
||||
"message": "Impossibile completare"
|
||||
},
|
||||
"genericError": {
|
||||
"message": "Qualcosa è andato storto nel raggiungere la tua istanza Memento."
|
||||
},
|
||||
"retry": {
|
||||
"message": "Riprova"
|
||||
},
|
||||
"errNoSelection": {
|
||||
"message": "Seleziona prima il testo o ritaglia l'intera pagina."
|
||||
},
|
||||
"errAnalyzeFailed": {
|
||||
"message": "Impossibile analizzare questa pagina."
|
||||
},
|
||||
"errSaveFailed": {
|
||||
"message": "Impossibile salvare la nota."
|
||||
},
|
||||
"errNetwork": {
|
||||
"message": "Problema di rete: controlla la connessione e l'URL Memento."
|
||||
},
|
||||
"bannerPickText": {
|
||||
"message": "Evidenzia il testo sulla pagina o ritaglia l'intera pagina."
|
||||
}
|
||||
}
|
||||
182
memento-note/extension/dist-firefox/_locales/ja/messages.json
Normal file
182
memento-note/extension/dist-firefox/_locales/ja/messages.json
Normal file
@@ -0,0 +1,182 @@
|
||||
{
|
||||
"extName": {
|
||||
"message": "モーメントウェブクリッパー"
|
||||
},
|
||||
"extDescription": {
|
||||
"message": "Web ページとハイライトされたテキストを Memento ノートブックにキャプチャします。独自の Memento サーバーに接続します。"
|
||||
},
|
||||
"extActionTitle": {
|
||||
"message": "モーメントにクリップ"
|
||||
},
|
||||
"webClipper": {
|
||||
"message": "ウェブクリッパー"
|
||||
},
|
||||
"connected": {
|
||||
"message": "接続済み"
|
||||
},
|
||||
"disconnected": {
|
||||
"message": "接続されていません"
|
||||
},
|
||||
"instanceSettings": {
|
||||
"message": "モーメントのURL"
|
||||
},
|
||||
"instanceUrlLabel": {
|
||||
"message": "Memento インスタンスの URL"
|
||||
},
|
||||
"presetProduction": {
|
||||
"message": "プロダクションプリセット・memento-note.com"
|
||||
},
|
||||
"applyReconnect": {
|
||||
"message": "適用して再接続する"
|
||||
},
|
||||
"openMomento": {
|
||||
"message": "モーメントを開く"
|
||||
},
|
||||
"settingsHint": {
|
||||
"message": "Memento サーバーの HTTPS (または LAN) URL を貼り付けます。このブラウザの Cookie がサインインを処理します。"
|
||||
},
|
||||
"footerVersion": {
|
||||
"message": "Memento Web クリッパー <<<バージョン>>>"
|
||||
},
|
||||
"errPermissionDenied": {
|
||||
"message": "Memento はこのタブにアクセスできません。キーボード/サイト拡張機能の権限を確認するか、サイド パネルを開きます。"
|
||||
},
|
||||
"notebookUnnamed": {
|
||||
"message": "無題のノート"
|
||||
},
|
||||
"noNotebooks": {
|
||||
"message": "まだノートはありません"
|
||||
},
|
||||
"readingTimeOne": {
|
||||
"message": "約 1 分で読めます"
|
||||
},
|
||||
"readingTimeOther": {
|
||||
"message": "約$COUNT$ 分読み取り",
|
||||
"placeholders": {
|
||||
"COUNT": {
|
||||
"content": "$1",
|
||||
"example": "5"
|
||||
}
|
||||
}
|
||||
},
|
||||
"selectionDetected": {
|
||||
"message": "選択が検出されました"
|
||||
},
|
||||
"ignore": {
|
||||
"message": "無視する"
|
||||
},
|
||||
"selectionHint": {
|
||||
"message": "ヒント: ページ上のテキストをハイライト表示して、正確な選択範囲をメモとしてクリップします。"
|
||||
},
|
||||
"clipSelection": {
|
||||
"message": "クリップの選択"
|
||||
},
|
||||
"clipPage": {
|
||||
"message": "このページをクリップします"
|
||||
},
|
||||
"saveLinkOnly": {
|
||||
"message": "リンクのみを保存"
|
||||
},
|
||||
"pageNotAccessible": {
|
||||
"message": "ここではクリップできません — このページは拡張機能へのアクセスをブロックしています。"
|
||||
},
|
||||
"errLoginRequired": {
|
||||
"message": "まずこのブラウザで Memento にサインインしてください。"
|
||||
},
|
||||
"errLoadNotebooks": {
|
||||
"message": "ノートブックをロードできませんでした。再接続してみてください。"
|
||||
},
|
||||
"notebooksLoaded": {
|
||||
"message": "ノートブックがロードされました"
|
||||
},
|
||||
"connecting": {
|
||||
"message": "接続中…"
|
||||
},
|
||||
"connectedToUrl": {
|
||||
"message": "$URL$ に接続しました",
|
||||
"placeholders": {
|
||||
"URL": {
|
||||
"content": "$1",
|
||||
"example": "https://memento-note.com"
|
||||
}
|
||||
}
|
||||
},
|
||||
"restrictedPage": {
|
||||
"message": "制限されたページ — Memento ツールバーまたはサイド パネルを介してクリップします。"
|
||||
},
|
||||
"destinationNotebook": {
|
||||
"message": "宛先ノートブック"
|
||||
},
|
||||
"activePage": {
|
||||
"message": "アクティブなページ"
|
||||
},
|
||||
"previewBeforeSave": {
|
||||
"message": "保存する前に確認してください"
|
||||
},
|
||||
"noteTitleLabel": {
|
||||
"message": "タイトル"
|
||||
},
|
||||
"excerptLabel": {
|
||||
"message": "抜粋"
|
||||
},
|
||||
"saveToMomento": {
|
||||
"message": "モーメントに保存"
|
||||
},
|
||||
"back": {
|
||||
"message": "戻る"
|
||||
},
|
||||
"analyzingSource": {
|
||||
"message": "ソースを分析中"
|
||||
},
|
||||
"statusAnalyzing": {
|
||||
"message": "分析中…"
|
||||
},
|
||||
"statusSaving": {
|
||||
"message": "保存中…"
|
||||
},
|
||||
"processingDetail": {
|
||||
"message": "タグ、意味の概要、および埋め込みを生成します。"
|
||||
},
|
||||
"noteSaved": {
|
||||
"message": "メモが保存されました"
|
||||
},
|
||||
"sentToNotebook": {
|
||||
"message": "$NOTEBOOK$ に保存されました",
|
||||
"placeholders": {
|
||||
"NOTEBOOK": {
|
||||
"content": "$1",
|
||||
"example": "Read later"
|
||||
}
|
||||
}
|
||||
},
|
||||
"viewInMomento": {
|
||||
"message": "モメントで見る"
|
||||
},
|
||||
"clipAnother": {
|
||||
"message": "別のページをクリップする"
|
||||
},
|
||||
"failure": {
|
||||
"message": "完了できませんでした"
|
||||
},
|
||||
"genericError": {
|
||||
"message": "Memento インスタンスに到達する際に問題が発生しました。"
|
||||
},
|
||||
"retry": {
|
||||
"message": "リトライ"
|
||||
},
|
||||
"errNoSelection": {
|
||||
"message": "最初にテキストを選択するか、ページ全体をクリップします。"
|
||||
},
|
||||
"errAnalyzeFailed": {
|
||||
"message": "このページを分析できませんでした。"
|
||||
},
|
||||
"errSaveFailed": {
|
||||
"message": "メモを保存できませんでした。"
|
||||
},
|
||||
"errNetwork": {
|
||||
"message": "ネットワークの問題 — 接続と Memento URL を確認してください。"
|
||||
},
|
||||
"bannerPickText": {
|
||||
"message": "ページ上のテキストを強調表示するか、ページ全体をクリップします。"
|
||||
}
|
||||
}
|
||||
182
memento-note/extension/dist-firefox/_locales/ko/messages.json
Normal file
182
memento-note/extension/dist-firefox/_locales/ko/messages.json
Normal file
@@ -0,0 +1,182 @@
|
||||
{
|
||||
"extName": {
|
||||
"message": "모멘토 웹 클리퍼"
|
||||
},
|
||||
"extDescription": {
|
||||
"message": "웹 페이지와 강조 표시된 텍스트를 Memento 노트북에 캡처하여 자체 Memento 서버에 연결합니다."
|
||||
},
|
||||
"extActionTitle": {
|
||||
"message": "순간에 클립"
|
||||
},
|
||||
"webClipper": {
|
||||
"message": "웹 클리퍼"
|
||||
},
|
||||
"connected": {
|
||||
"message": "연결됨"
|
||||
},
|
||||
"disconnected": {
|
||||
"message": "연결되지 않음"
|
||||
},
|
||||
"instanceSettings": {
|
||||
"message": "모멘토 URL"
|
||||
},
|
||||
"instanceUrlLabel": {
|
||||
"message": "귀하의 Memento 인스턴스 URL"
|
||||
},
|
||||
"presetProduction": {
|
||||
"message": "프로덕션 프리셋 · memento-note.com"
|
||||
},
|
||||
"applyReconnect": {
|
||||
"message": "적용하고 다시 연결하세요"
|
||||
},
|
||||
"openMomento": {
|
||||
"message": "모멘토 열기"
|
||||
},
|
||||
"settingsHint": {
|
||||
"message": "Memento 서버의 HTTPS(또는 LAN) URL을 붙여넣습니다. 이 브라우저의 쿠키는 로그인을 처리합니다."
|
||||
},
|
||||
"footerVersion": {
|
||||
"message": "Memento Web Clipper <<<버전>>>"
|
||||
},
|
||||
"errPermissionDenied": {
|
||||
"message": "Memento는 이 탭에 접근할 수 없습니다. 키보드/사이트 확장 권한을 확인하거나 측면 패널을 엽니다."
|
||||
},
|
||||
"notebookUnnamed": {
|
||||
"message": "제목 없는 노트"
|
||||
},
|
||||
"noNotebooks": {
|
||||
"message": "아직 노트가 없습니다."
|
||||
},
|
||||
"readingTimeOne": {
|
||||
"message": "~1분 읽기"
|
||||
},
|
||||
"readingTimeOther": {
|
||||
"message": "대략. $COUNT$분 읽음",
|
||||
"placeholders": {
|
||||
"COUNT": {
|
||||
"content": "$1",
|
||||
"example": "5"
|
||||
}
|
||||
}
|
||||
},
|
||||
"selectionDetected": {
|
||||
"message": "선택 항목이 감지되었습니다."
|
||||
},
|
||||
"ignore": {
|
||||
"message": "무시하다"
|
||||
},
|
||||
"selectionHint": {
|
||||
"message": "팁: 페이지의 텍스트를 강조 표시하여 정확한 선택 항목을 메모로 자릅니다."
|
||||
},
|
||||
"clipSelection": {
|
||||
"message": "클립 선택"
|
||||
},
|
||||
"clipPage": {
|
||||
"message": "이 페이지 클립"
|
||||
},
|
||||
"saveLinkOnly": {
|
||||
"message": "링크만 저장"
|
||||
},
|
||||
"pageNotAccessible": {
|
||||
"message": "여기서 클립할 수 없습니다. 이 페이지는 확장 프로그램 액세스를 차단합니다."
|
||||
},
|
||||
"errLoginRequired": {
|
||||
"message": "먼저 이 브라우저에서 Memento에 로그인하세요."
|
||||
},
|
||||
"errLoadNotebooks": {
|
||||
"message": "노트북을 로드할 수 없습니다. 다시 연결해 보세요."
|
||||
},
|
||||
"notebooksLoaded": {
|
||||
"message": "노트북이 로드되었습니다."
|
||||
},
|
||||
"connecting": {
|
||||
"message": "연결 중…"
|
||||
},
|
||||
"connectedToUrl": {
|
||||
"message": "$URL$에 연결됨",
|
||||
"placeholders": {
|
||||
"URL": {
|
||||
"content": "$1",
|
||||
"example": "https://memento-note.com"
|
||||
}
|
||||
}
|
||||
},
|
||||
"restrictedPage": {
|
||||
"message": "제한된 페이지 — Memento 도구 모음 또는 측면 패널을 통해 클립합니다."
|
||||
},
|
||||
"destinationNotebook": {
|
||||
"message": "대상 노트북"
|
||||
},
|
||||
"activePage": {
|
||||
"message": "활성 페이지"
|
||||
},
|
||||
"previewBeforeSave": {
|
||||
"message": "저장하기 전에 검토하세요"
|
||||
},
|
||||
"noteTitleLabel": {
|
||||
"message": "제목"
|
||||
},
|
||||
"excerptLabel": {
|
||||
"message": "발췌"
|
||||
},
|
||||
"saveToMomento": {
|
||||
"message": "모멘토에 저장"
|
||||
},
|
||||
"back": {
|
||||
"message": "뒤쪽에"
|
||||
},
|
||||
"analyzingSource": {
|
||||
"message": "소스 분석 중"
|
||||
},
|
||||
"statusAnalyzing": {
|
||||
"message": "분석 중…"
|
||||
},
|
||||
"statusSaving": {
|
||||
"message": "절약…"
|
||||
},
|
||||
"processingDetail": {
|
||||
"message": "태그, 의미 요약 및 임베딩을 생성합니다."
|
||||
},
|
||||
"noteSaved": {
|
||||
"message": "메모가 저장되었습니다."
|
||||
},
|
||||
"sentToNotebook": {
|
||||
"message": "$NOTEBOOK$에 저장됨",
|
||||
"placeholders": {
|
||||
"NOTEBOOK": {
|
||||
"content": "$1",
|
||||
"example": "Read later"
|
||||
}
|
||||
}
|
||||
},
|
||||
"viewInMomento": {
|
||||
"message": "Memento에서 보기"
|
||||
},
|
||||
"clipAnother": {
|
||||
"message": "다른 페이지 자르기"
|
||||
},
|
||||
"failure": {
|
||||
"message": "완료할 수 없습니다."
|
||||
},
|
||||
"genericError": {
|
||||
"message": "Memento 인스턴스에 연결하는 데 문제가 발생했습니다."
|
||||
},
|
||||
"retry": {
|
||||
"message": "다시 해 보다"
|
||||
},
|
||||
"errNoSelection": {
|
||||
"message": "먼저 텍스트를 선택하거나 전체 페이지를 자릅니다."
|
||||
},
|
||||
"errAnalyzeFailed": {
|
||||
"message": "이 페이지를 분석할 수 없습니다."
|
||||
},
|
||||
"errSaveFailed": {
|
||||
"message": "메모를 저장할 수 없습니다."
|
||||
},
|
||||
"errNetwork": {
|
||||
"message": "네트워크 문제 - 연결 및 Memento URL을 확인하세요."
|
||||
},
|
||||
"bannerPickText": {
|
||||
"message": "페이지의 텍스트를 강조 표시하거나 전체 페이지를 자릅니다."
|
||||
}
|
||||
}
|
||||
182
memento-note/extension/dist-firefox/_locales/nl/messages.json
Normal file
182
memento-note/extension/dist-firefox/_locales/nl/messages.json
Normal file
@@ -0,0 +1,182 @@
|
||||
{
|
||||
"extName": {
|
||||
"message": "Memento Webclipper"
|
||||
},
|
||||
"extDescription": {
|
||||
"message": "Leg webpagina's en gemarkeerde tekst vast in uw Memento-notebooks - maakt verbinding met uw eigen Memento-server."
|
||||
},
|
||||
"extActionTitle": {
|
||||
"message": "Clip naar Memento"
|
||||
},
|
||||
"webClipper": {
|
||||
"message": "Webclipper"
|
||||
},
|
||||
"connected": {
|
||||
"message": "Aangesloten"
|
||||
},
|
||||
"disconnected": {
|
||||
"message": "Niet verbonden"
|
||||
},
|
||||
"instanceSettings": {
|
||||
"message": "Memento-URL"
|
||||
},
|
||||
"instanceUrlLabel": {
|
||||
"message": "Uw Memento-instantie-URL"
|
||||
},
|
||||
"presetProduction": {
|
||||
"message": "Productievoorinstelling · memento-note.com"
|
||||
},
|
||||
"applyReconnect": {
|
||||
"message": "Toepassen en opnieuw verbinden"
|
||||
},
|
||||
"openMomento": {
|
||||
"message": "Memento openen"
|
||||
},
|
||||
"settingsHint": {
|
||||
"message": "Plak de HTTPS (of LAN) URL van uw Memento-server. Cookies in deze browser zorgen voor het inloggen."
|
||||
},
|
||||
"footerVersion": {
|
||||
"message": "Memento Web Clipper <<<VERSIE>>>"
|
||||
},
|
||||
"errPermissionDenied": {
|
||||
"message": "Memento heeft geen toegang tot dit tabblad. Controleer de rechten voor toetsenbord-/site-extensies — of open het zijpaneel."
|
||||
},
|
||||
"notebookUnnamed": {
|
||||
"message": "Naamloos notitieboekje"
|
||||
},
|
||||
"noNotebooks": {
|
||||
"message": "Nog geen notitieboekjes"
|
||||
},
|
||||
"readingTimeOne": {
|
||||
"message": "~1 minuut lezen"
|
||||
},
|
||||
"readingTimeOther": {
|
||||
"message": "Ongeveer. $COUNT$ min gelezen",
|
||||
"placeholders": {
|
||||
"COUNT": {
|
||||
"content": "$1",
|
||||
"example": "5"
|
||||
}
|
||||
}
|
||||
},
|
||||
"selectionDetected": {
|
||||
"message": "Selectie gedetecteerd"
|
||||
},
|
||||
"ignore": {
|
||||
"message": "negeren"
|
||||
},
|
||||
"selectionHint": {
|
||||
"message": "Tip: markeer tekst op de pagina om een precieze selectie als notitie te knippen."
|
||||
},
|
||||
"clipSelection": {
|
||||
"message": "Clipselectie"
|
||||
},
|
||||
"clipPage": {
|
||||
"message": "Knip deze pagina uit"
|
||||
},
|
||||
"saveLinkOnly": {
|
||||
"message": "Alleen link opslaan"
|
||||
},
|
||||
"pageNotAccessible": {
|
||||
"message": "Kan hier niet knippen: deze pagina blokkeert de toegang tot extensies."
|
||||
},
|
||||
"errLoginRequired": {
|
||||
"message": "Meld u eerst aan bij Memento in deze browser."
|
||||
},
|
||||
"errLoadNotebooks": {
|
||||
"message": "Kan notitieboekjes niet laden. Probeer opnieuw verbinding te maken."
|
||||
},
|
||||
"notebooksLoaded": {
|
||||
"message": "Notitieboekjes geladen"
|
||||
},
|
||||
"connecting": {
|
||||
"message": "Verbinden…"
|
||||
},
|
||||
"connectedToUrl": {
|
||||
"message": "Verbonden met $URL$",
|
||||
"placeholders": {
|
||||
"URL": {
|
||||
"content": "$1",
|
||||
"example": "https://memento-note.com"
|
||||
}
|
||||
}
|
||||
},
|
||||
"restrictedPage": {
|
||||
"message": "Beperkte pagina — clip via de Memento-werkbalk of het zijpaneel."
|
||||
},
|
||||
"destinationNotebook": {
|
||||
"message": "Bestemmingsnotitieboekje"
|
||||
},
|
||||
"activePage": {
|
||||
"message": "Actieve pagina"
|
||||
},
|
||||
"previewBeforeSave": {
|
||||
"message": "Controleer voordat u opslaat"
|
||||
},
|
||||
"noteTitleLabel": {
|
||||
"message": "Titel"
|
||||
},
|
||||
"excerptLabel": {
|
||||
"message": "Uittreksel"
|
||||
},
|
||||
"saveToMomento": {
|
||||
"message": "Opslaan in Memento"
|
||||
},
|
||||
"back": {
|
||||
"message": "Rug"
|
||||
},
|
||||
"analyzingSource": {
|
||||
"message": "Bron analyseren"
|
||||
},
|
||||
"statusAnalyzing": {
|
||||
"message": "Analyseren…"
|
||||
},
|
||||
"statusSaving": {
|
||||
"message": "Besparing…"
|
||||
},
|
||||
"processingDetail": {
|
||||
"message": "Tags, een semantische samenvatting en insluitingen genereren."
|
||||
},
|
||||
"noteSaved": {
|
||||
"message": "Notitie opgeslagen"
|
||||
},
|
||||
"sentToNotebook": {
|
||||
"message": "Opgeslagen in $NOTEBOOK$",
|
||||
"placeholders": {
|
||||
"NOTEBOOK": {
|
||||
"content": "$1",
|
||||
"example": "Read later"
|
||||
}
|
||||
}
|
||||
},
|
||||
"viewInMomento": {
|
||||
"message": "Bekijk in Memento"
|
||||
},
|
||||
"clipAnother": {
|
||||
"message": "Knip nog een pagina uit"
|
||||
},
|
||||
"failure": {
|
||||
"message": "Kon niet voltooien"
|
||||
},
|
||||
"genericError": {
|
||||
"message": "Er is iets misgegaan bij het bereiken van uw Memento-instantie."
|
||||
},
|
||||
"retry": {
|
||||
"message": "Opnieuw proberen"
|
||||
},
|
||||
"errNoSelection": {
|
||||
"message": "Selecteer eerst tekst of knip de volledige pagina uit."
|
||||
},
|
||||
"errAnalyzeFailed": {
|
||||
"message": "Kan deze pagina niet analyseren."
|
||||
},
|
||||
"errSaveFailed": {
|
||||
"message": "Kan uw notitie niet opslaan."
|
||||
},
|
||||
"errNetwork": {
|
||||
"message": "Netwerkprobleem: controleer uw verbinding en Memento-URL."
|
||||
},
|
||||
"bannerPickText": {
|
||||
"message": "Markeer tekst op de pagina of knip de hele pagina uit."
|
||||
}
|
||||
}
|
||||
182
memento-note/extension/dist-firefox/_locales/pl/messages.json
Normal file
182
memento-note/extension/dist-firefox/_locales/pl/messages.json
Normal file
@@ -0,0 +1,182 @@
|
||||
{
|
||||
"extName": {
|
||||
"message": "Narzędzie do strzyżenia sieci Memento"
|
||||
},
|
||||
"extDescription": {
|
||||
"message": "Przechwytuj strony internetowe i zaznaczony tekst do swoich notatników Memento — łączy się z Twoim własnym serwerem Memento."
|
||||
},
|
||||
"extActionTitle": {
|
||||
"message": "Klip do Memento"
|
||||
},
|
||||
"webClipper": {
|
||||
"message": "Obcinacz sieci"
|
||||
},
|
||||
"connected": {
|
||||
"message": "Połączony"
|
||||
},
|
||||
"disconnected": {
|
||||
"message": "Nie podłączony"
|
||||
},
|
||||
"instanceSettings": {
|
||||
"message": "Adres URL chwili"
|
||||
},
|
||||
"instanceUrlLabel": {
|
||||
"message": "Adres URL Twojej instancji Memento"
|
||||
},
|
||||
"presetProduction": {
|
||||
"message": "Wstępne ustawienia produkcyjne · memento-note.com"
|
||||
},
|
||||
"applyReconnect": {
|
||||
"message": "Zastosuj i połącz ponownie"
|
||||
},
|
||||
"openMomento": {
|
||||
"message": "Otwórz Memento"
|
||||
},
|
||||
"settingsHint": {
|
||||
"message": "Wklej adres URL HTTPS (lub LAN) swojego serwera Memento. Pliki cookie w tej przeglądarce obsługują logowanie."
|
||||
},
|
||||
"footerVersion": {
|
||||
"message": "Memento Web Clipper <<<WERSJA>>>"
|
||||
},
|
||||
"errPermissionDenied": {
|
||||
"message": "Memento nie ma dostępu do tej karty. Sprawdź uprawnienia rozszerzenia klawiatury/witryny — lub otwórz Panel boczny."
|
||||
},
|
||||
"notebookUnnamed": {
|
||||
"message": "Notatnik bez tytułu"
|
||||
},
|
||||
"noNotebooks": {
|
||||
"message": "Nie ma jeszcze żadnych notatników"
|
||||
},
|
||||
"readingTimeOne": {
|
||||
"message": "~1 minuta czytania"
|
||||
},
|
||||
"readingTimeOther": {
|
||||
"message": "Około. $COUNT$ min odczytu",
|
||||
"placeholders": {
|
||||
"COUNT": {
|
||||
"content": "$1",
|
||||
"example": "5"
|
||||
}
|
||||
}
|
||||
},
|
||||
"selectionDetected": {
|
||||
"message": "Wykryto wybór"
|
||||
},
|
||||
"ignore": {
|
||||
"message": "ignorować"
|
||||
},
|
||||
"selectionHint": {
|
||||
"message": "Wskazówka: zaznacz tekst na stronie, aby wyciąć dokładne zaznaczenie jako notatkę."
|
||||
},
|
||||
"clipSelection": {
|
||||
"message": "Wybór klipu"
|
||||
},
|
||||
"clipPage": {
|
||||
"message": "Przytnij tę stronę"
|
||||
},
|
||||
"saveLinkOnly": {
|
||||
"message": "Zapisz tylko link"
|
||||
},
|
||||
"pageNotAccessible": {
|
||||
"message": "Nie można tutaj przyciąć — ta strona blokuje dostęp do rozszerzenia."
|
||||
},
|
||||
"errLoginRequired": {
|
||||
"message": "Najpierw zaloguj się do Memento w tej przeglądarce."
|
||||
},
|
||||
"errLoadNotebooks": {
|
||||
"message": "Nie udało się załadować notatników. Spróbuj połączyć się ponownie."
|
||||
},
|
||||
"notebooksLoaded": {
|
||||
"message": "Notatniki załadowane"
|
||||
},
|
||||
"connecting": {
|
||||
"message": "Złączony…"
|
||||
},
|
||||
"connectedToUrl": {
|
||||
"message": "Połączono z $URL$",
|
||||
"placeholders": {
|
||||
"URL": {
|
||||
"content": "$1",
|
||||
"example": "https://memento-note.com"
|
||||
}
|
||||
}
|
||||
},
|
||||
"restrictedPage": {
|
||||
"message": "Strona z ograniczeniami — klip za pomocą paska narzędzi Memento lub panelu bocznego."
|
||||
},
|
||||
"destinationNotebook": {
|
||||
"message": "Notatnik docelowy"
|
||||
},
|
||||
"activePage": {
|
||||
"message": "Aktywna strona"
|
||||
},
|
||||
"previewBeforeSave": {
|
||||
"message": "Przejrzyj przed zapisaniem"
|
||||
},
|
||||
"noteTitleLabel": {
|
||||
"message": "Tytuł"
|
||||
},
|
||||
"excerptLabel": {
|
||||
"message": "Fragment"
|
||||
},
|
||||
"saveToMomento": {
|
||||
"message": "Zapisz w Memento"
|
||||
},
|
||||
"back": {
|
||||
"message": "Z powrotem"
|
||||
},
|
||||
"analyzingSource": {
|
||||
"message": "Analizowanie źródła"
|
||||
},
|
||||
"statusAnalyzing": {
|
||||
"message": "Analizuję…"
|
||||
},
|
||||
"statusSaving": {
|
||||
"message": "Oszczędność…"
|
||||
},
|
||||
"processingDetail": {
|
||||
"message": "Generowanie tagów, podsumowań semantycznych i osadzania."
|
||||
},
|
||||
"noteSaved": {
|
||||
"message": "Uwaga zapisana"
|
||||
},
|
||||
"sentToNotebook": {
|
||||
"message": "Zapisano w $NOTEBOOK$",
|
||||
"placeholders": {
|
||||
"NOTEBOOK": {
|
||||
"content": "$1",
|
||||
"example": "Read later"
|
||||
}
|
||||
}
|
||||
},
|
||||
"viewInMomento": {
|
||||
"message": "Zobacz w Memento"
|
||||
},
|
||||
"clipAnother": {
|
||||
"message": "Wytnij kolejną stronę"
|
||||
},
|
||||
"failure": {
|
||||
"message": "Nie udało się ukończyć"
|
||||
},
|
||||
"genericError": {
|
||||
"message": "Coś poszło nie tak podczas docierania do Twojej instancji Memento."
|
||||
},
|
||||
"retry": {
|
||||
"message": "Spróbować ponownie"
|
||||
},
|
||||
"errNoSelection": {
|
||||
"message": "Najpierw zaznacz tekst lub wytnij całą stronę."
|
||||
},
|
||||
"errAnalyzeFailed": {
|
||||
"message": "Nie można przeanalizować tej strony."
|
||||
},
|
||||
"errSaveFailed": {
|
||||
"message": "Nie można zapisać notatki."
|
||||
},
|
||||
"errNetwork": {
|
||||
"message": "Problem z siecią — sprawdź swoje połączenie i adres URL Memento."
|
||||
},
|
||||
"bannerPickText": {
|
||||
"message": "Zaznacz tekst na stronie lub przytnij całą stronę."
|
||||
}
|
||||
}
|
||||
182
memento-note/extension/dist-firefox/_locales/pt/messages.json
Normal file
182
memento-note/extension/dist-firefox/_locales/pt/messages.json
Normal file
@@ -0,0 +1,182 @@
|
||||
{
|
||||
"extName": {
|
||||
"message": "Memento Web Clipper"
|
||||
},
|
||||
"extDescription": {
|
||||
"message": "Capture páginas da web e texto destacado em seus blocos de anotações Memento – conecte-se ao seu próprio servidor Memento."
|
||||
},
|
||||
"extActionTitle": {
|
||||
"message": "Clipe para Memento"
|
||||
},
|
||||
"webClipper": {
|
||||
"message": "Clipper da Web"
|
||||
},
|
||||
"connected": {
|
||||
"message": "Conectado"
|
||||
},
|
||||
"disconnected": {
|
||||
"message": "Não conectado"
|
||||
},
|
||||
"instanceSettings": {
|
||||
"message": "URL do momento"
|
||||
},
|
||||
"instanceUrlLabel": {
|
||||
"message": "URL da sua instância do Memento"
|
||||
},
|
||||
"presetProduction": {
|
||||
"message": "Predefinição de produção · memento-note.com"
|
||||
},
|
||||
"applyReconnect": {
|
||||
"message": "Aplicar e reconectar"
|
||||
},
|
||||
"openMomento": {
|
||||
"message": "Memento aberto"
|
||||
},
|
||||
"settingsHint": {
|
||||
"message": "Cole o URL HTTPS (ou LAN) do seu servidor Memento. Os cookies neste navegador controlam o login."
|
||||
},
|
||||
"footerVersion": {
|
||||
"message": "Memento Web Clipper <<<VERSÃO>>>"
|
||||
},
|
||||
"errPermissionDenied": {
|
||||
"message": "Memento não pode acessar esta guia. Verifique as permissões de extensão de teclado/site – ou abra o painel lateral."
|
||||
},
|
||||
"notebookUnnamed": {
|
||||
"message": "Caderno sem título"
|
||||
},
|
||||
"noNotebooks": {
|
||||
"message": "Ainda não há cadernos"
|
||||
},
|
||||
"readingTimeOne": {
|
||||
"message": "~1 minuto de leitura"
|
||||
},
|
||||
"readingTimeOther": {
|
||||
"message": "Aprox. $COUNT$ minutos de leitura",
|
||||
"placeholders": {
|
||||
"COUNT": {
|
||||
"content": "$1",
|
||||
"example": "5"
|
||||
}
|
||||
}
|
||||
},
|
||||
"selectionDetected": {
|
||||
"message": "Seleção detectada"
|
||||
},
|
||||
"ignore": {
|
||||
"message": "ignorar"
|
||||
},
|
||||
"selectionHint": {
|
||||
"message": "Dica: destaque o texto na página para recortar uma seleção precisa como uma nota."
|
||||
},
|
||||
"clipSelection": {
|
||||
"message": "Seleção de clipe"
|
||||
},
|
||||
"clipPage": {
|
||||
"message": "Recorte esta página"
|
||||
},
|
||||
"saveLinkOnly": {
|
||||
"message": "Salvar apenas link"
|
||||
},
|
||||
"pageNotAccessible": {
|
||||
"message": "Não é possível recortar aqui – esta página bloqueia o acesso à extensão."
|
||||
},
|
||||
"errLoginRequired": {
|
||||
"message": "Faça login no Memento neste navegador primeiro."
|
||||
},
|
||||
"errLoadNotebooks": {
|
||||
"message": "Não foi possível carregar os notebooks. Tente reconectar."
|
||||
},
|
||||
"notebooksLoaded": {
|
||||
"message": "Cadernos carregados"
|
||||
},
|
||||
"connecting": {
|
||||
"message": "Conectando…"
|
||||
},
|
||||
"connectedToUrl": {
|
||||
"message": "Conectado a $URL$",
|
||||
"placeholders": {
|
||||
"URL": {
|
||||
"content": "$1",
|
||||
"example": "https://memento-note.com"
|
||||
}
|
||||
}
|
||||
},
|
||||
"restrictedPage": {
|
||||
"message": "Página restrita – recorte por meio da barra de ferramentas do Memento ou do painel lateral."
|
||||
},
|
||||
"destinationNotebook": {
|
||||
"message": "Caderno de destino"
|
||||
},
|
||||
"activePage": {
|
||||
"message": "Página ativa"
|
||||
},
|
||||
"previewBeforeSave": {
|
||||
"message": "Revise antes de salvar"
|
||||
},
|
||||
"noteTitleLabel": {
|
||||
"message": "Título"
|
||||
},
|
||||
"excerptLabel": {
|
||||
"message": "Trecho"
|
||||
},
|
||||
"saveToMomento": {
|
||||
"message": "Salvar no Memento"
|
||||
},
|
||||
"back": {
|
||||
"message": "Voltar"
|
||||
},
|
||||
"analyzingSource": {
|
||||
"message": "Analisando fonte"
|
||||
},
|
||||
"statusAnalyzing": {
|
||||
"message": "Analisando…"
|
||||
},
|
||||
"statusSaving": {
|
||||
"message": "Salvando…"
|
||||
},
|
||||
"processingDetail": {
|
||||
"message": "Geração de tags, resumo semântico e incorporações."
|
||||
},
|
||||
"noteSaved": {
|
||||
"message": "Nota salva"
|
||||
},
|
||||
"sentToNotebook": {
|
||||
"message": "Salvo em $NOTEBOOK$",
|
||||
"placeholders": {
|
||||
"NOTEBOOK": {
|
||||
"content": "$1",
|
||||
"example": "Read later"
|
||||
}
|
||||
}
|
||||
},
|
||||
"viewInMomento": {
|
||||
"message": "Ver em Memento"
|
||||
},
|
||||
"clipAnother": {
|
||||
"message": "Recortar outra página"
|
||||
},
|
||||
"failure": {
|
||||
"message": "Não foi possível concluir"
|
||||
},
|
||||
"genericError": {
|
||||
"message": "Algo deu errado ao chegar à sua instância do Memento."
|
||||
},
|
||||
"retry": {
|
||||
"message": "Tentar novamente"
|
||||
},
|
||||
"errNoSelection": {
|
||||
"message": "Selecione o texto primeiro ou recorte a página inteira."
|
||||
},
|
||||
"errAnalyzeFailed": {
|
||||
"message": "Não foi possível analisar esta página."
|
||||
},
|
||||
"errSaveFailed": {
|
||||
"message": "Não foi possível salvar sua nota."
|
||||
},
|
||||
"errNetwork": {
|
||||
"message": "Problema de rede – verifique sua conexão e URL do Memento."
|
||||
},
|
||||
"bannerPickText": {
|
||||
"message": "Destaque o texto na página ou recorte a página inteira."
|
||||
}
|
||||
}
|
||||
182
memento-note/extension/dist-firefox/_locales/ru/messages.json
Normal file
182
memento-note/extension/dist-firefox/_locales/ru/messages.json
Normal file
@@ -0,0 +1,182 @@
|
||||
{
|
||||
"extName": {
|
||||
"message": "Веб-клипер Memento"
|
||||
},
|
||||
"extDescription": {
|
||||
"message": "Сохраняйте веб-страницы и выделенный текст в свои блокноты Memento — подключайтесь к вашему собственному серверу Memento."
|
||||
},
|
||||
"extActionTitle": {
|
||||
"message": "Клип на Memento"
|
||||
},
|
||||
"webClipper": {
|
||||
"message": "Веб-клипер"
|
||||
},
|
||||
"connected": {
|
||||
"message": "Подключено"
|
||||
},
|
||||
"disconnected": {
|
||||
"message": "Не подключено"
|
||||
},
|
||||
"instanceSettings": {
|
||||
"message": "URL-адрес момента"
|
||||
},
|
||||
"instanceUrlLabel": {
|
||||
"message": "URL-адрес вашего экземпляра Memento"
|
||||
},
|
||||
"presetProduction": {
|
||||
"message": "Настройки производства · memento-note.com"
|
||||
},
|
||||
"applyReconnect": {
|
||||
"message": "Подать заявку и повторно подключиться"
|
||||
},
|
||||
"openMomento": {
|
||||
"message": "Открыть Моменто"
|
||||
},
|
||||
"settingsHint": {
|
||||
"message": "Вставьте URL-адрес HTTPS (или LAN) вашего сервера Memento. Файлы cookie в этом браузере обрабатывают вход в систему."
|
||||
},
|
||||
"footerVersion": {
|
||||
"message": "Memento Web Clipper <<<ВЕРСИЯ>>>"
|
||||
},
|
||||
"errPermissionDenied": {
|
||||
"message": "Memento не имеет доступа к этой вкладке. Проверьте разрешения для расширения клавиатуры/сайта или откройте боковую панель."
|
||||
},
|
||||
"notebookUnnamed": {
|
||||
"message": "Блокнот без названия"
|
||||
},
|
||||
"noNotebooks": {
|
||||
"message": "Блокнотов пока нет"
|
||||
},
|
||||
"readingTimeOne": {
|
||||
"message": "~1 минута чтения"
|
||||
},
|
||||
"readingTimeOther": {
|
||||
"message": "Прибл. $COUNT$ мин чтения",
|
||||
"placeholders": {
|
||||
"COUNT": {
|
||||
"content": "$1",
|
||||
"example": "5"
|
||||
}
|
||||
}
|
||||
},
|
||||
"selectionDetected": {
|
||||
"message": "Выбор обнаружен"
|
||||
},
|
||||
"ignore": {
|
||||
"message": "игнорировать"
|
||||
},
|
||||
"selectionHint": {
|
||||
"message": "Совет: выделите текст на странице, чтобы выделить его в виде заметки."
|
||||
},
|
||||
"clipSelection": {
|
||||
"message": "Выбор клипа"
|
||||
},
|
||||
"clipPage": {
|
||||
"message": "Вырезать эту страницу"
|
||||
},
|
||||
"saveLinkOnly": {
|
||||
"message": "Сохранить только ссылку"
|
||||
},
|
||||
"pageNotAccessible": {
|
||||
"message": "Невозможно обрезать здесь — эта страница блокирует доступ к расширению."
|
||||
},
|
||||
"errLoginRequired": {
|
||||
"message": "Сначала войдите в Memento в этом браузере."
|
||||
},
|
||||
"errLoadNotebooks": {
|
||||
"message": "Не удалось загрузить блокноты. Попробуйте переподключиться."
|
||||
},
|
||||
"notebooksLoaded": {
|
||||
"message": "Ноутбуки загружены"
|
||||
},
|
||||
"connecting": {
|
||||
"message": "Подключение…"
|
||||
},
|
||||
"connectedToUrl": {
|
||||
"message": "Подключено к $URL$",
|
||||
"placeholders": {
|
||||
"URL": {
|
||||
"content": "$1",
|
||||
"example": "https://memento-note.com"
|
||||
}
|
||||
}
|
||||
},
|
||||
"restrictedPage": {
|
||||
"message": "Страница с ограниченным доступом — вырезайте с помощью панели инструментов Memento или боковой панели."
|
||||
},
|
||||
"destinationNotebook": {
|
||||
"message": "Блокнот назначения"
|
||||
},
|
||||
"activePage": {
|
||||
"message": "Активная страница"
|
||||
},
|
||||
"previewBeforeSave": {
|
||||
"message": "Проверьте перед сохранением"
|
||||
},
|
||||
"noteTitleLabel": {
|
||||
"message": "Заголовок"
|
||||
},
|
||||
"excerptLabel": {
|
||||
"message": "Отрывок"
|
||||
},
|
||||
"saveToMomento": {
|
||||
"message": "Сохранить в Моменто"
|
||||
},
|
||||
"back": {
|
||||
"message": "Назад"
|
||||
},
|
||||
"analyzingSource": {
|
||||
"message": "Анализ источника"
|
||||
},
|
||||
"statusAnalyzing": {
|
||||
"message": "Анализ…"
|
||||
},
|
||||
"statusSaving": {
|
||||
"message": "Сохранение…"
|
||||
},
|
||||
"processingDetail": {
|
||||
"message": "Генерация тегов, семантического резюме и вложений."
|
||||
},
|
||||
"noteSaved": {
|
||||
"message": "Заметка сохранена."
|
||||
},
|
||||
"sentToNotebook": {
|
||||
"message": "Сохранено в $NOTEBOOK$",
|
||||
"placeholders": {
|
||||
"NOTEBOOK": {
|
||||
"content": "$1",
|
||||
"example": "Read later"
|
||||
}
|
||||
}
|
||||
},
|
||||
"viewInMomento": {
|
||||
"message": "Посмотреть в Моменто"
|
||||
},
|
||||
"clipAnother": {
|
||||
"message": "Вырезать другую страницу"
|
||||
},
|
||||
"failure": {
|
||||
"message": "Не удалось завершить"
|
||||
},
|
||||
"genericError": {
|
||||
"message": "Что-то пошло не так при получении вашего экземпляра Memento."
|
||||
},
|
||||
"retry": {
|
||||
"message": "Повторить попытку"
|
||||
},
|
||||
"errNoSelection": {
|
||||
"message": "Сначала выделите текст или вырежьте всю страницу."
|
||||
},
|
||||
"errAnalyzeFailed": {
|
||||
"message": "Не удалось проанализировать эту страницу."
|
||||
},
|
||||
"errSaveFailed": {
|
||||
"message": "Не удалось сохранить заметку."
|
||||
},
|
||||
"errNetwork": {
|
||||
"message": "Проблема с сетью. Проверьте подключение и URL-адрес Memento."
|
||||
},
|
||||
"bannerPickText": {
|
||||
"message": "Выделите текст на странице или вырежьте всю страницу."
|
||||
}
|
||||
}
|
||||
182
memento-note/extension/dist-firefox/_locales/zh/messages.json
Normal file
182
memento-note/extension/dist-firefox/_locales/zh/messages.json
Normal file
@@ -0,0 +1,182 @@
|
||||
{
|
||||
"extName": {
|
||||
"message": "Memento 网页剪辑器"
|
||||
},
|
||||
"extDescription": {
|
||||
"message": "将网页和突出显示的文本捕获到您的 Memento 笔记本中 — 连接到您自己的 Memento 服务器。"
|
||||
},
|
||||
"extActionTitle": {
|
||||
"message": "剪辑到时刻"
|
||||
},
|
||||
"webClipper": {
|
||||
"message": "网页剪辑器"
|
||||
},
|
||||
"connected": {
|
||||
"message": "已连接"
|
||||
},
|
||||
"disconnected": {
|
||||
"message": "未连接"
|
||||
},
|
||||
"instanceSettings": {
|
||||
"message": "时刻网址"
|
||||
},
|
||||
"instanceUrlLabel": {
|
||||
"message": "您的 Memento 实例 URL"
|
||||
},
|
||||
"presetProduction": {
|
||||
"message": "制作预设 · memento-note.com"
|
||||
},
|
||||
"applyReconnect": {
|
||||
"message": "应用并重新连接"
|
||||
},
|
||||
"openMomento": {
|
||||
"message": "打开时刻"
|
||||
},
|
||||
"settingsHint": {
|
||||
"message": "粘贴 Memento 服务器的 HTTPS(或 LAN)URL。此浏览器中的 Cookie 处理登录。"
|
||||
},
|
||||
"footerVersion": {
|
||||
"message": "Memento Web Clipper <<<版本>>>"
|
||||
},
|
||||
"errPermissionDenied": {
|
||||
"message": "Memento 无法访问此选项卡。检查键盘/站点扩展权限 - 或打开侧面板。"
|
||||
},
|
||||
"notebookUnnamed": {
|
||||
"message": "无标题笔记本"
|
||||
},
|
||||
"noNotebooks": {
|
||||
"message": "还没有笔记本"
|
||||
},
|
||||
"readingTimeOne": {
|
||||
"message": "阅读约 1 分钟"
|
||||
},
|
||||
"readingTimeOther": {
|
||||
"message": "大约。 $COUNT$ 最少阅读次数",
|
||||
"placeholders": {
|
||||
"COUNT": {
|
||||
"content": "$1",
|
||||
"example": "5"
|
||||
}
|
||||
}
|
||||
},
|
||||
"selectionDetected": {
|
||||
"message": "检测到选择"
|
||||
},
|
||||
"ignore": {
|
||||
"message": "忽略"
|
||||
},
|
||||
"selectionHint": {
|
||||
"message": "提示:突出显示页面上的文本以将精确的选择剪辑为注释。"
|
||||
},
|
||||
"clipSelection": {
|
||||
"message": "剪辑选择"
|
||||
},
|
||||
"clipPage": {
|
||||
"message": "剪辑此页"
|
||||
},
|
||||
"saveLinkOnly": {
|
||||
"message": "仅保存链接"
|
||||
},
|
||||
"pageNotAccessible": {
|
||||
"message": "此处无法剪辑 — 此页面阻止扩展程序访问。"
|
||||
},
|
||||
"errLoginRequired": {
|
||||
"message": "请先在此浏览器中登录 Memento。"
|
||||
},
|
||||
"errLoadNotebooks": {
|
||||
"message": "无法加载笔记本。尝试重新连接。"
|
||||
},
|
||||
"notebooksLoaded": {
|
||||
"message": "笔记本已加载"
|
||||
},
|
||||
"connecting": {
|
||||
"message": "正在连接…"
|
||||
},
|
||||
"connectedToUrl": {
|
||||
"message": "连接到 $URL$",
|
||||
"placeholders": {
|
||||
"URL": {
|
||||
"content": "$1",
|
||||
"example": "https://memento-note.com"
|
||||
}
|
||||
}
|
||||
},
|
||||
"restrictedPage": {
|
||||
"message": "受限页面 — 通过 Memento 工具栏或侧面板进行剪辑。"
|
||||
},
|
||||
"destinationNotebook": {
|
||||
"message": "目的地笔记本"
|
||||
},
|
||||
"activePage": {
|
||||
"message": "活动页面"
|
||||
},
|
||||
"previewBeforeSave": {
|
||||
"message": "保存前查看"
|
||||
},
|
||||
"noteTitleLabel": {
|
||||
"message": "标题"
|
||||
},
|
||||
"excerptLabel": {
|
||||
"message": "摘抄"
|
||||
},
|
||||
"saveToMomento": {
|
||||
"message": "保存到时刻"
|
||||
},
|
||||
"back": {
|
||||
"message": "后退"
|
||||
},
|
||||
"analyzingSource": {
|
||||
"message": "分析来源"
|
||||
},
|
||||
"statusAnalyzing": {
|
||||
"message": "正在分析……"
|
||||
},
|
||||
"statusSaving": {
|
||||
"message": "保存…"
|
||||
},
|
||||
"processingDetail": {
|
||||
"message": "生成标签、语义摘要和嵌入。"
|
||||
},
|
||||
"noteSaved": {
|
||||
"message": "注释已保存"
|
||||
},
|
||||
"sentToNotebook": {
|
||||
"message": "保存至$NOTEBOOK$",
|
||||
"placeholders": {
|
||||
"NOTEBOOK": {
|
||||
"content": "$1",
|
||||
"example": "Read later"
|
||||
}
|
||||
}
|
||||
},
|
||||
"viewInMomento": {
|
||||
"message": "在 Memento 中查看"
|
||||
},
|
||||
"clipAnother": {
|
||||
"message": "剪辑另一页"
|
||||
},
|
||||
"failure": {
|
||||
"message": "无法完成"
|
||||
},
|
||||
"genericError": {
|
||||
"message": "您的 Memento 实例出现问题。"
|
||||
},
|
||||
"retry": {
|
||||
"message": "重试"
|
||||
},
|
||||
"errNoSelection": {
|
||||
"message": "首先选择文本,或剪辑整个页面。"
|
||||
},
|
||||
"errAnalyzeFailed": {
|
||||
"message": "无法分析此页面。"
|
||||
},
|
||||
"errSaveFailed": {
|
||||
"message": "无法保存您的笔记。"
|
||||
},
|
||||
"errNetwork": {
|
||||
"message": "网络问题 — 检查您的连接和 Memento URL。"
|
||||
},
|
||||
"bannerPickText": {
|
||||
"message": "突出显示页面上的文本,或剪辑整个页面。"
|
||||
}
|
||||
}
|
||||
23
memento-note/extension/dist-firefox/background.js
Normal file
23
memento-note/extension/dist-firefox/background.js
Normal file
@@ -0,0 +1,23 @@
|
||||
/** Service worker — ouvre le panneau latéral (Chrome Side Panel ou Firefox sidebar) au clic sur l'icône. */
|
||||
const extApi = (typeof browser !== 'undefined' && browser.sidebarAction) ? browser : chrome
|
||||
|
||||
if (extApi.sidePanel?.setPanelBehavior) {
|
||||
// Chrome MV3 : Side Panel API
|
||||
extApi.sidePanel.setPanelBehavior({ openPanelOnActionClick: true }).catch(() => {})
|
||||
} else if (extApi.sidebarAction?.open) {
|
||||
// Firefox MV3 : sidebarAction
|
||||
extApi.action?.onClicked?.addListener?.(() => {
|
||||
extApi.sidebarAction.open().catch(() => {})
|
||||
})
|
||||
}
|
||||
|
||||
extApi.runtime?.onInstalled?.addListener?.(() => {
|
||||
if (extApi.sidePanel?.setPanelBehavior) {
|
||||
extApi.sidePanel.setPanelBehavior({ openPanelOnActionClick: true }).catch(() => {})
|
||||
}
|
||||
})
|
||||
extApi.runtime?.onStartup?.addListener?.(() => {
|
||||
if (extApi.sidePanel?.setPanelBehavior) {
|
||||
extApi.sidePanel.setPanelBehavior({ openPanelOnActionClick: true }).catch(() => {})
|
||||
}
|
||||
})
|
||||
211
memento-note/extension/dist-firefox/content.js
Normal file
211
memento-note/extension/dist-firefox/content.js
Normal file
@@ -0,0 +1,211 @@
|
||||
/**
|
||||
* Content script Memento — sélection live, surlignage, communication avec le side panel.
|
||||
* Injecté automatiquement sur http(s) ; ré-injecté à la demande si l’onglet était déjà ouvert.
|
||||
*/
|
||||
;(function initMementoClipperContent() {
|
||||
if (globalThis.__mementoClipperContent) return
|
||||
globalThis.__mementoClipperContent = true
|
||||
|
||||
const HIGHLIGHT_ID = 'memento-clipper-highlight-root'
|
||||
const BANNER_ID = 'memento-clipper-banner-root'
|
||||
const STYLE_ID = 'memento-clipper-styles'
|
||||
|
||||
let pickMode = false
|
||||
let debounceTimer = null
|
||||
|
||||
function getSelectionText() {
|
||||
return window.getSelection()?.toString().trim() || ''
|
||||
}
|
||||
|
||||
function getPageMeta() {
|
||||
const dir =
|
||||
document.documentElement.getAttribute('dir') ||
|
||||
document.body?.getAttribute('dir') ||
|
||||
''
|
||||
const lang = (
|
||||
document.documentElement.getAttribute('lang') ||
|
||||
document.body?.getAttribute('lang') ||
|
||||
''
|
||||
).split('-')[0]
|
||||
return {
|
||||
text: getSelectionText(),
|
||||
dir,
|
||||
lang,
|
||||
url: location.href,
|
||||
title: document.title,
|
||||
}
|
||||
}
|
||||
|
||||
function broadcastSelection() {
|
||||
clearTimeout(debounceTimer)
|
||||
debounceTimer = setTimeout(() => {
|
||||
const payload = { type: 'SELECTION_CHANGED', ...getPageMeta() }
|
||||
try {
|
||||
chrome.runtime.sendMessage(payload).catch(() => {})
|
||||
} catch {
|
||||
/* ignore */
|
||||
}
|
||||
if (pickMode) paintHighlight()
|
||||
}, 80)
|
||||
}
|
||||
|
||||
function removeHighlight() {
|
||||
document.getElementById(HIGHLIGHT_ID)?.remove()
|
||||
}
|
||||
|
||||
function paintHighlight() {
|
||||
removeHighlight()
|
||||
const sel = window.getSelection()
|
||||
if (!sel || sel.isCollapsed || !sel.rangeCount) return
|
||||
|
||||
let range
|
||||
try {
|
||||
range = sel.getRangeAt(0)
|
||||
} catch {
|
||||
return
|
||||
}
|
||||
|
||||
const host = document.createElement('div')
|
||||
host.id = HIGHLIGHT_ID
|
||||
host.setAttribute('aria-hidden', 'true')
|
||||
host.style.cssText =
|
||||
'position:fixed;inset:0;pointer-events:none;z-index:2147483644;overflow:hidden;'
|
||||
|
||||
for (const rect of range.getClientRects()) {
|
||||
if (rect.width < 2 || rect.height < 2) continue
|
||||
const box = document.createElement('div')
|
||||
box.style.cssText = [
|
||||
'position:fixed',
|
||||
`left:${rect.left - 2}px`,
|
||||
`top:${rect.top - 1}px`,
|
||||
`width:${rect.width + 4}px`,
|
||||
`height:${rect.height + 2}px`,
|
||||
'background:rgba(164,113,72,0.28)',
|
||||
'border-radius:3px',
|
||||
'box-shadow:0 0 0 1px rgba(164,113,72,0.35)',
|
||||
'transition:opacity 0.15s ease',
|
||||
].join(';')
|
||||
host.appendChild(box)
|
||||
}
|
||||
|
||||
if (host.childNodes.length) document.documentElement.appendChild(host)
|
||||
}
|
||||
|
||||
function ensureStyles() {
|
||||
if (document.getElementById(STYLE_ID)) return
|
||||
const style = document.createElement('style')
|
||||
style.id = STYLE_ID
|
||||
style.textContent = `
|
||||
html.memento-clipper-pick ::selection {
|
||||
background: rgba(164, 113, 72, 0.45) !important;
|
||||
color: inherit !important;
|
||||
}
|
||||
html.memento-clipper-pick {
|
||||
scroll-behavior: auto;
|
||||
}
|
||||
`
|
||||
document.documentElement.appendChild(style)
|
||||
}
|
||||
|
||||
function removeBanner() {
|
||||
document.getElementById(BANNER_ID)?.remove()
|
||||
}
|
||||
|
||||
function ensureBanner() {
|
||||
if (document.getElementById(BANNER_ID)) return
|
||||
|
||||
const bannerText =
|
||||
(typeof chrome !== 'undefined' && chrome.i18n?.getMessage?.('bannerPickText')) ||
|
||||
'Highlight the text to clip'
|
||||
|
||||
const host = document.createElement('div')
|
||||
host.id = BANNER_ID
|
||||
host.style.cssText =
|
||||
'all:initial;position:fixed;top:16px;left:50%;transform:translateX(-50%);z-index:2147483647;pointer-events:none;font-family:Inter,system-ui,sans-serif;'
|
||||
|
||||
const shadow = host.attachShadow({ mode: 'open' })
|
||||
shadow.innerHTML = `
|
||||
<style>
|
||||
.pill {
|
||||
display: flex; align-items: center; gap: 10px;
|
||||
padding: 10px 18px; border-radius: 999px;
|
||||
background: #1c1c1c; color: #faf9f5;
|
||||
box-shadow: 0 12px 32px rgba(0,0,0,0.22);
|
||||
font-size: 12px; font-weight: 600;
|
||||
letter-spacing: 0.02em;
|
||||
animation: slideIn 0.35s cubic-bezier(0.22,1,0.36,1);
|
||||
}
|
||||
.logo {
|
||||
width: 22px; height: 22px; border-radius: 7px;
|
||||
background: #faf9f5; color: #1c1c1c;
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
font-family: Georgia, serif; font-weight: 900; font-size: 12px;
|
||||
}
|
||||
.dot {
|
||||
width: 8px; height: 8px; border-radius: 50%;
|
||||
background: #a47148; animation: pulse 1.2s ease infinite;
|
||||
}
|
||||
@keyframes slideIn {
|
||||
from { opacity: 0; transform: translateY(-8px); }
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
}
|
||||
@keyframes pulse {
|
||||
0%, 100% { opacity: 1; transform: scale(1); }
|
||||
50% { opacity: 0.5; transform: scale(0.85); }
|
||||
}
|
||||
</style>
|
||||
<div class="pill">
|
||||
<span class="logo">M</span>
|
||||
<span class="dot"></span>
|
||||
<span>${bannerText.replace(/</g, '<')}</span>
|
||||
</div>
|
||||
`
|
||||
document.documentElement.appendChild(host)
|
||||
}
|
||||
|
||||
function setPickMode(enabled) {
|
||||
pickMode = !!enabled
|
||||
ensureStyles()
|
||||
if (pickMode) {
|
||||
document.documentElement.classList.add('memento-clipper-pick')
|
||||
ensureBanner()
|
||||
paintHighlight()
|
||||
} else {
|
||||
document.documentElement.classList.remove('memento-clipper-pick')
|
||||
removeBanner()
|
||||
removeHighlight()
|
||||
}
|
||||
}
|
||||
|
||||
function onScrollOrResize() {
|
||||
if (pickMode) paintHighlight()
|
||||
}
|
||||
|
||||
document.addEventListener('selectionchange', broadcastSelection)
|
||||
document.addEventListener('mouseup', broadcastSelection)
|
||||
document.addEventListener('keyup', broadcastSelection)
|
||||
window.addEventListener('scroll', onScrollOrResize, { passive: true, capture: true })
|
||||
window.addEventListener('resize', onScrollOrResize, { passive: true })
|
||||
|
||||
chrome.runtime.onMessage.addListener((message, _sender, sendResponse) => {
|
||||
if (message?.type === 'PING') {
|
||||
sendResponse({ ok: true })
|
||||
return true
|
||||
}
|
||||
if (message?.type === 'GET_CONTEXT') {
|
||||
sendResponse({
|
||||
html: document.documentElement.outerHTML,
|
||||
...getPageMeta(),
|
||||
})
|
||||
return true
|
||||
}
|
||||
if (message?.type === 'SET_PICK_MODE') {
|
||||
setPickMode(!!message.enabled)
|
||||
sendResponse({ ok: true, pickMode })
|
||||
return true
|
||||
}
|
||||
return false
|
||||
})
|
||||
|
||||
broadcastSelection()
|
||||
})()
|
||||
47
memento-note/extension/dist-firefox/i18n.js
Normal file
47
memento-note/extension/dist-firefox/i18n.js
Normal file
@@ -0,0 +1,47 @@
|
||||
/** Helpers i18n — langue UI Chrome (chrome.i18n.getUILanguage). */
|
||||
function t(key, ...subs) {
|
||||
const msg = chrome.i18n.getMessage(key, subs)
|
||||
return msg || key
|
||||
}
|
||||
|
||||
function uiLocaleTag() {
|
||||
return (chrome.i18n.getUILanguage() || 'en').replace('_', '-').split('-')[0]
|
||||
}
|
||||
|
||||
function isUiRtl() {
|
||||
return ['ar', 'fa', 'he', 'ur'].includes(uiLocaleTag())
|
||||
}
|
||||
|
||||
function applyDocumentLocale() {
|
||||
const lang = uiLocaleTag()
|
||||
document.documentElement.lang = lang
|
||||
document.documentElement.dir = isUiRtl() ? 'rtl' : 'ltr'
|
||||
document.body?.classList.toggle('ui-rtl', isUiRtl())
|
||||
}
|
||||
|
||||
function applyShellI18n() {
|
||||
document.title = t('extName')
|
||||
const brandSub = document.querySelector('.brand-sub')
|
||||
if (brandSub) brandSub.textContent = t('webClipper')
|
||||
if (typeof els !== 'undefined' && els.connLabel) {
|
||||
els.connLabel.textContent = t('connected')
|
||||
}
|
||||
if (typeof els !== 'undefined' && els.settingsBtn) {
|
||||
els.settingsBtn.title = t('instanceSettings')
|
||||
els.settingsBtn.setAttribute('aria-label', t('instanceSettings'))
|
||||
}
|
||||
const urlLabel = document.getElementById('instanceUrlLabel') || document.querySelector('#settingsPanel .field > span')
|
||||
if (urlLabel) urlLabel.textContent = t('instanceUrlLabel')
|
||||
const presetProd = document.querySelector('[data-url="https://memento-note.com"]')
|
||||
if (presetProd) presetProd.textContent = t('presetProduction')
|
||||
if (typeof els !== 'undefined' && els.applyInstanceBtn) {
|
||||
els.applyInstanceBtn.textContent = t('applyReconnect')
|
||||
}
|
||||
if (typeof els !== 'undefined' && els.openLoginBtn) {
|
||||
els.openLoginBtn.textContent = `${t('openMomento')} ↗`
|
||||
}
|
||||
const hint = document.querySelector('.settings-hint')
|
||||
if (hint) hint.textContent = t('settingsHint')
|
||||
const footer = document.querySelector('.footer-meta')
|
||||
if (footer) footer.textContent = t('footerVersion')
|
||||
}
|
||||
BIN
memento-note/extension/dist-firefox/icon-128.png
Normal file
BIN
memento-note/extension/dist-firefox/icon-128.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.5 KiB |
BIN
memento-note/extension/dist-firefox/icon-16.png
Normal file
BIN
memento-note/extension/dist-firefox/icon-16.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 236 B |
10
memento-note/extension/dist-firefox/icon-192.svg
Normal file
10
memento-note/extension/dist-firefox/icon-192.svg
Normal file
@@ -0,0 +1,10 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 192 192">
|
||||
<rect width="192" height="192" fill="#1C1C1C" rx="40"/>
|
||||
<g transform="translate(96,96)">
|
||||
<rect x="-34" y="-34" width="68" height="68" rx="5" fill="white"/>
|
||||
<path d="M34,34 L34,11 L11,34 Z" fill="#1C1C1C" opacity="0.28"/>
|
||||
<line x1="-23" y1="-17" x2="23" y2="-17" stroke="#e5e7eb" stroke-width="3" stroke-linecap="round"/>
|
||||
<line x1="-23" y1="-4" x2="23" y2="-4" stroke="#e5e7eb" stroke-width="3" stroke-linecap="round"/>
|
||||
<line x1="-23" y1="9" x2="11" y2="9" stroke="#e5e7eb" stroke-width="3" stroke-linecap="round"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 616 B |
BIN
memento-note/extension/dist-firefox/icon-48.png
Normal file
BIN
memento-note/extension/dist-firefox/icon-48.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 587 B |
10
memento-note/extension/dist-firefox/icon-512.svg
Normal file
10
memento-note/extension/dist-firefox/icon-512.svg
Normal file
@@ -0,0 +1,10 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
|
||||
<rect width="512" height="512" fill="#1C1C1C" rx="100"/>
|
||||
<g transform="translate(256,256)">
|
||||
<rect x="-90" y="-90" width="180" height="180" rx="12" fill="white"/>
|
||||
<path d="M90,90 L90,30 L30,90 Z" fill="#1C1C1C" opacity="0.28"/>
|
||||
<line x1="-60" y1="-45" x2="60" y2="-45" stroke="#e5e7eb" stroke-width="8" stroke-linecap="round"/>
|
||||
<line x1="-60" y1="-10" x2="60" y2="-10" stroke="#e5e7eb" stroke-width="8" stroke-linecap="round"/>
|
||||
<line x1="-60" y1="25" x2="30" y2="25" stroke="#e5e7eb" stroke-width="8" stroke-linecap="round"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 626 B |
65
memento-note/extension/dist-firefox/manifest.json
Normal file
65
memento-note/extension/dist-firefox/manifest.json
Normal file
@@ -0,0 +1,65 @@
|
||||
{
|
||||
"manifest_version": 3,
|
||||
"name": "__MSG_extName__",
|
||||
"version": "0.4.0",
|
||||
"description": "__MSG_extDescription__",
|
||||
"default_locale": "en",
|
||||
"permissions": [
|
||||
"activeTab",
|
||||
"scripting",
|
||||
"storage",
|
||||
"sidePanel",
|
||||
"tabs"
|
||||
],
|
||||
"host_permissions": [
|
||||
"https://memento-note.com/*",
|
||||
"https://www.memento-note.com/*"
|
||||
],
|
||||
"optional_host_permissions": [
|
||||
"https://*/*"
|
||||
],
|
||||
"background": {
|
||||
"service_worker": "background.js"
|
||||
},
|
||||
"sidebar_action": {
|
||||
"default_title": "__MSG_extActionTitle__",
|
||||
"default_panel": "sidepanel.html",
|
||||
"default_icon": {
|
||||
"16": "icon-16.png",
|
||||
"48": "icon-48.png",
|
||||
"128": "icon-128.png"
|
||||
}
|
||||
},
|
||||
"browser_specific_settings": {
|
||||
"gecko": {
|
||||
"id": "memento-clipper@memento-note.com",
|
||||
"strict_min_version": "115.0"
|
||||
}
|
||||
},
|
||||
"content_scripts": [
|
||||
{
|
||||
"matches": [
|
||||
"https://memento-note.com/*",
|
||||
"https://www.memento-note.com/*"
|
||||
],
|
||||
"js": [
|
||||
"content.js"
|
||||
],
|
||||
"run_at": "document_idle",
|
||||
"all_frames": false
|
||||
}
|
||||
],
|
||||
"action": {
|
||||
"default_title": "__MSG_extActionTitle__",
|
||||
"default_icon": {
|
||||
"16": "icon-16.png",
|
||||
"48": "icon-48.png",
|
||||
"128": "icon-128.png"
|
||||
}
|
||||
},
|
||||
"icons": {
|
||||
"16": "icon-16.png",
|
||||
"48": "icon-48.png",
|
||||
"128": "icon-128.png"
|
||||
}
|
||||
}
|
||||
507
memento-note/extension/dist-firefox/sidepanel.css
Normal file
507
memento-note/extension/dist-firefox/sidepanel.css
Normal file
@@ -0,0 +1,507 @@
|
||||
:root {
|
||||
--ink: #1c1c1c;
|
||||
--paper: #faf9f5;
|
||||
--card: #ffffff;
|
||||
--muted: #6b7280;
|
||||
--border: #e8e4dc;
|
||||
--accent: #a47148;
|
||||
--accent-soft: rgba(164, 113, 72, 0.12);
|
||||
--accent-glow: rgba(164, 113, 72, 0.35);
|
||||
--success: #10b981;
|
||||
--danger: #ef4444;
|
||||
--shadow: 0 18px 40px rgba(28, 28, 28, 0.08);
|
||||
--radius: 14px;
|
||||
--radius-sm: 10px;
|
||||
}
|
||||
|
||||
* { box-sizing: border-box; }
|
||||
|
||||
html, body {
|
||||
margin: 0;
|
||||
min-height: 100%;
|
||||
font-family: 'Inter', system-ui, -apple-system, sans-serif;
|
||||
font-size: 13px;
|
||||
color: var(--ink);
|
||||
background: var(--paper);
|
||||
}
|
||||
|
||||
.shell {
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background: var(--paper);
|
||||
}
|
||||
|
||||
.header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 16px 18px;
|
||||
background: linear-gradient(180deg, #fff 0%, #fcfcfa 100%);
|
||||
border-bottom: 1px solid var(--border);
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.brand { display: flex; align-items: center; gap: 10px; }
|
||||
.brand-logo {
|
||||
width: 34px; height: 34px; border-radius: 11px;
|
||||
background: var(--ink); color: #faf9f5;
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
font-family: Georgia, 'Times New Roman', serif;
|
||||
font-weight: 900; font-size: 16px;
|
||||
box-shadow: 0 4px 14px rgba(28, 28, 28, 0.18);
|
||||
}
|
||||
.brand-text { line-height: 1.1; }
|
||||
.brand-name {
|
||||
display: block; font-size: 14px; font-weight: 700;
|
||||
font-family: Georgia, 'Times New Roman', serif;
|
||||
}
|
||||
.brand-sub {
|
||||
display: block; font-size: 9px; letter-spacing: 0.16em;
|
||||
text-transform: uppercase; color: var(--accent); font-weight: 700;
|
||||
}
|
||||
|
||||
.icon-btn {
|
||||
width: 34px; height: 34px; border-radius: 10px;
|
||||
border: 1px solid var(--border); background: #fff;
|
||||
color: var(--muted); cursor: pointer;
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
transition: border-color 0.15s, color 0.15s, background 0.15s;
|
||||
}
|
||||
.icon-btn:hover {
|
||||
border-color: var(--accent);
|
||||
color: var(--accent);
|
||||
background: var(--accent-soft);
|
||||
}
|
||||
|
||||
.header-right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.conn-badge {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
font-size: 9px;
|
||||
font-weight: 800;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.1em;
|
||||
color: var(--muted);
|
||||
}
|
||||
.conn-dot {
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
border-radius: 50%;
|
||||
background: #10b981;
|
||||
}
|
||||
|
||||
.settings-panel {
|
||||
padding: 14px 18px;
|
||||
background: #fff;
|
||||
border-bottom: 1px solid var(--border);
|
||||
}
|
||||
.settings-panel[hidden] { display: none !important; }
|
||||
.settings-hint {
|
||||
margin: 8px 0 0;
|
||||
font-size: 11px;
|
||||
color: var(--muted);
|
||||
line-height: 1.5;
|
||||
}
|
||||
.settings-hint code {
|
||||
font-size: 10px;
|
||||
background: var(--paper);
|
||||
padding: 1px 4px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
.settings-status {
|
||||
margin: 10px 0 0;
|
||||
font-size: 11px;
|
||||
line-height: 1.45;
|
||||
}
|
||||
.settings-status.is-ok { color: #059669; }
|
||||
.settings-status.is-error { color: #dc2626; }
|
||||
|
||||
.preset-row {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 6px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
.preset-btn {
|
||||
border: 1px solid var(--border);
|
||||
background: var(--paper);
|
||||
border-radius: 999px;
|
||||
padding: 6px 10px;
|
||||
font-size: 10px;
|
||||
font-weight: 700;
|
||||
cursor: pointer;
|
||||
color: var(--muted);
|
||||
}
|
||||
.preset-btn:hover {
|
||||
border-color: var(--accent);
|
||||
color: var(--accent);
|
||||
background: var(--accent-soft);
|
||||
}
|
||||
|
||||
.settings-actions {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
.field { display: flex; flex-direction: column; gap: 6px; }
|
||||
.field span {
|
||||
font-size: 9px; text-transform: uppercase; letter-spacing: 0.12em;
|
||||
color: var(--muted); font-weight: 700;
|
||||
}
|
||||
input[type="url"],
|
||||
input[type="text"],
|
||||
.notebook-select {
|
||||
width: 100%; padding: 10px 12px; border: 1px solid var(--border);
|
||||
border-radius: var(--radius-sm); background: var(--paper);
|
||||
font-family: inherit; font-size: 12px;
|
||||
}
|
||||
input[type="url"]:focus,
|
||||
input[type="text"]:focus,
|
||||
.notebook-select:focus {
|
||||
outline: none; border-color: var(--accent);
|
||||
box-shadow: 0 0 0 3px var(--accent-soft);
|
||||
}
|
||||
.notebook-select {
|
||||
background: #fff;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.main {
|
||||
flex: 1;
|
||||
padding: 16px 18px 20px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 14px;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.main > .actions {
|
||||
margin-top: auto;
|
||||
}
|
||||
|
||||
.footer {
|
||||
padding: 10px 18px 14px;
|
||||
border-top: 1px solid var(--border);
|
||||
background: #fff;
|
||||
text-align: center;
|
||||
}
|
||||
.footer-meta { font-size: 9px; color: #9ca3af; letter-spacing: 0.06em; }
|
||||
|
||||
.label {
|
||||
font-size: 9px; text-transform: uppercase; letter-spacing: 0.14em;
|
||||
color: var(--muted); font-weight: 700; margin-bottom: 8px; display: block;
|
||||
}
|
||||
|
||||
.auth-hint {
|
||||
padding: 12px 14px;
|
||||
border-radius: var(--radius);
|
||||
background: #fffbeb;
|
||||
border: 1px solid #fde68a;
|
||||
font-size: 11px;
|
||||
color: #92400e;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.page-card {
|
||||
padding: 14px; border: 1px solid var(--border); border-radius: var(--radius);
|
||||
background: #fff;
|
||||
box-shadow: 0 1px 0 rgba(255,255,255,0.8) inset;
|
||||
}
|
||||
.page-card .sub {
|
||||
font-size: 9px; text-transform: uppercase; letter-spacing: 0.14em;
|
||||
color: var(--muted); font-weight: 700; display: block; margin-bottom: 8px;
|
||||
}
|
||||
.page-row { display: flex; gap: 10px; align-items: flex-start; min-width: 0; }
|
||||
.page-row img {
|
||||
width: 20px; height: 20px; border-radius: 5px;
|
||||
flex-shrink: 0; margin-top: 2px;
|
||||
}
|
||||
.page-text { min-width: 0; flex: 1; }
|
||||
.page-row .title {
|
||||
font-size: 12px; font-weight: 700; line-height: 1.45;
|
||||
display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden;
|
||||
unicode-bidi: plaintext;
|
||||
}
|
||||
.page-row .url {
|
||||
font-size: 10px; color: var(--muted); margin-top: 4px;
|
||||
white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
|
||||
direction: ltr; text-align: left;
|
||||
}
|
||||
|
||||
.text-rtl {
|
||||
direction: rtl;
|
||||
text-align: right;
|
||||
font-family: 'Vazirmatn', 'Inter', sans-serif;
|
||||
unicode-bidi: plaintext;
|
||||
}
|
||||
|
||||
.selection-panel {
|
||||
border-radius: var(--radius);
|
||||
border: 1px solid var(--border);
|
||||
background: #fff;
|
||||
overflow: hidden;
|
||||
min-height: 140px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.selection-panel.has-text {
|
||||
border-color: #bae6fd;
|
||||
background: rgba(14, 165, 233, 0.05);
|
||||
box-shadow: 0 0 0 1px rgba(14, 165, 233, 0.12);
|
||||
}
|
||||
.selection-hint {
|
||||
padding: 16px;
|
||||
border: 1px dashed var(--border);
|
||||
border-radius: var(--radius);
|
||||
text-align: center;
|
||||
background: #fff;
|
||||
}
|
||||
.selection-hint p {
|
||||
margin: 0;
|
||||
font-size: 11px;
|
||||
color: var(--muted);
|
||||
line-height: 1.55;
|
||||
}
|
||||
|
||||
.selection-head {
|
||||
display: flex; align-items: center; justify-content: space-between;
|
||||
padding: 12px 14px;
|
||||
background: linear-gradient(180deg, #fff 0%, #fdfcfa 100%);
|
||||
border-bottom: 1px solid var(--border);
|
||||
}
|
||||
.selection-head .status {
|
||||
display: flex; align-items: center; gap: 8px;
|
||||
font-size: 10px; font-weight: 800; text-transform: uppercase;
|
||||
letter-spacing: 0.08em; color: var(--muted);
|
||||
}
|
||||
.selection-head .status.live { color: #0284c7; }
|
||||
.selection-head .count {
|
||||
font-size: 10px; font-weight: 700; color: var(--muted);
|
||||
background: var(--paper); padding: 4px 8px; border-radius: 999px;
|
||||
}
|
||||
.selection-head .count.active { color: var(--accent); background: var(--accent-soft); }
|
||||
|
||||
.selection-body {
|
||||
flex: 1;
|
||||
padding: 14px;
|
||||
font-size: 13px;
|
||||
line-height: 1.75;
|
||||
color: rgba(28, 28, 28, 0.88);
|
||||
max-height: 220px;
|
||||
overflow-y: auto;
|
||||
unicode-bidi: plaintext;
|
||||
border-inline-start: 3px solid transparent;
|
||||
}
|
||||
.selection-panel.has-text .selection-body {
|
||||
border-inline-start-color: #38bdf8;
|
||||
padding-inline-start: 16px;
|
||||
font-style: italic;
|
||||
font-size: 12px;
|
||||
max-height: 150px;
|
||||
}
|
||||
|
||||
.pulse-dot {
|
||||
width: 7px; height: 7px; border-radius: 50%; background: var(--accent);
|
||||
animation: pulse 1.4s ease infinite;
|
||||
}
|
||||
.pulse-dot.sky { background: #0ea5e9; }
|
||||
@keyframes pulse { 0%,100%{opacity:1;transform:scale(1)} 50%{opacity:.4;transform:scale(.85)} }
|
||||
|
||||
.clear-btn {
|
||||
border: none; background: none; font-size: 10px;
|
||||
color: var(--muted); cursor: pointer; font-weight: 600;
|
||||
padding: 4px 6px; border-radius: 6px;
|
||||
}
|
||||
.clear-btn:hover { color: var(--ink); background: var(--paper); }
|
||||
|
||||
.actions {
|
||||
display: flex; flex-direction: column; gap: 10px;
|
||||
margin-top: auto; padding-top: 6px;
|
||||
}
|
||||
.btn {
|
||||
padding: 14px 16px; border-radius: var(--radius); border: none; cursor: pointer;
|
||||
font-weight: 700; font-size: 10px; text-transform: uppercase;
|
||||
letter-spacing: 0.1em;
|
||||
display: flex; align-items: center; justify-content: center; gap: 8px;
|
||||
transition: transform 0.12s ease, opacity 0.12s ease, filter 0.12s ease;
|
||||
}
|
||||
.btn:active { transform: scale(0.98); }
|
||||
.btn:disabled {
|
||||
opacity: 0.42; cursor: not-allowed; transform: none;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
.btn-primary {
|
||||
background: var(--ink); color: #fff;
|
||||
box-shadow: 0 10px 24px rgba(28, 28, 28, 0.18);
|
||||
}
|
||||
.btn-primary:hover:not(:disabled) { opacity: 0.94; }
|
||||
.btn-sky {
|
||||
background: #0284c7;
|
||||
color: #fff;
|
||||
box-shadow: 0 10px 22px rgba(2, 132, 199, 0.22);
|
||||
}
|
||||
.btn-sky:hover:not(:disabled) { background: #0369a1; }
|
||||
.btn-secondary {
|
||||
background: #f3f4f6;
|
||||
color: #374151;
|
||||
box-shadow: none;
|
||||
}
|
||||
.btn-secondary:hover:not(:disabled) { background: #e5e7eb; }
|
||||
.btn-sm {
|
||||
padding: 10px 12px;
|
||||
font-size: 10px;
|
||||
}
|
||||
.btn-danger { background: var(--danger); color: #fff; }
|
||||
.btn-link.link-only {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 6px;
|
||||
}
|
||||
.btn-link {
|
||||
background: none; border: none; color: var(--muted); font-size: 11px;
|
||||
cursor: pointer; padding: 8px; font-weight: 500;
|
||||
}
|
||||
.btn-link:hover { color: var(--ink); text-decoration: underline; }
|
||||
.btn-icon { width: 14px; height: 14px; display: inline-flex; }
|
||||
|
||||
.center-state {
|
||||
flex: 1; display: flex; flex-direction: column; align-items: center;
|
||||
justify-content: center; text-align: center; gap: 14px; padding: 32px 12px;
|
||||
min-height: 280px;
|
||||
}
|
||||
.spinner-wrap { position: relative; width: 52px; height: 52px; }
|
||||
.spinner-ring {
|
||||
position: absolute; inset: 0; border-radius: 50%;
|
||||
border: 1px solid var(--border); animation: ping 1.2s ease infinite;
|
||||
}
|
||||
@keyframes ping { 0%{transform:scale(1);opacity:.6} 100%{transform:scale(1.35);opacity:0} }
|
||||
.spinner {
|
||||
position: absolute; inset: 6px;
|
||||
border: 3px solid var(--border); border-top-color: var(--accent);
|
||||
border-radius: 50%; animation: spin 0.75s linear infinite;
|
||||
}
|
||||
@keyframes spin { to { transform: rotate(360deg); } }
|
||||
|
||||
.state-title {
|
||||
font-size: 10px; font-weight: 800; text-transform: uppercase;
|
||||
letter-spacing: 0.14em; color: var(--muted);
|
||||
}
|
||||
.state-sub { font-size: 15px; font-weight: 700; color: var(--ink); }
|
||||
.state-detail {
|
||||
font-size: 11px; color: var(--muted); max-width: 280px;
|
||||
line-height: 1.55; margin: 0 auto;
|
||||
}
|
||||
|
||||
.success-icon, .error-icon {
|
||||
width: 58px; height: 58px; border-radius: 50%;
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
font-size: 26px; font-weight: 700;
|
||||
}
|
||||
.success-icon {
|
||||
background: rgba(16, 185, 129, 0.1);
|
||||
border: 1px solid rgba(16, 185, 129, 0.25); color: var(--success);
|
||||
}
|
||||
.error-icon { background: #fef2f2; color: var(--danger); }
|
||||
|
||||
.badge-ok {
|
||||
display: inline-block; margin-bottom: 8px;
|
||||
font-size: 9px; background: rgba(16, 185, 129, 0.12);
|
||||
color: #059669; font-weight: 800; padding: 3px 8px; border-radius: 6px;
|
||||
text-transform: uppercase; letter-spacing: 0.1em;
|
||||
}
|
||||
.note-title {
|
||||
font-size: 15px; font-weight: 700;
|
||||
font-family: Georgia, 'Times New Roman', serif;
|
||||
line-height: 1.35; margin-top: 6px;
|
||||
unicode-bidi: plaintext;
|
||||
}
|
||||
.tags {
|
||||
display: flex; flex-wrap: wrap; gap: 6px; justify-content: center;
|
||||
padding-top: 14px; border-top: 1px solid var(--border); margin-top: 10px;
|
||||
width: 100%;
|
||||
}
|
||||
.tag-chip {
|
||||
font-size: 9px; font-weight: 800; text-transform: uppercase;
|
||||
letter-spacing: 0.06em; color: var(--accent);
|
||||
background: var(--accent-soft); border: 1px solid rgba(164, 113, 72, 0.2);
|
||||
padding: 5px 10px; border-radius: 999px;
|
||||
}
|
||||
|
||||
.restricted-note {
|
||||
padding: 14px; border-radius: var(--radius);
|
||||
background: #fef2f2; border: 1px solid #fecaca;
|
||||
font-size: 11px; color: #991b1b; line-height: 1.5;
|
||||
}
|
||||
|
||||
.confirm-panel {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
}
|
||||
.summary-preview {
|
||||
margin: 0;
|
||||
font-size: 12px;
|
||||
color: var(--muted);
|
||||
line-height: 1.55;
|
||||
font-style: italic;
|
||||
}
|
||||
.excerpt-preview {
|
||||
padding: 12px 14px;
|
||||
border-radius: var(--radius-sm);
|
||||
border: 1px solid var(--border);
|
||||
background: #fff;
|
||||
font-size: 12px;
|
||||
line-height: 1.65;
|
||||
max-height: 150px;
|
||||
overflow-y: auto;
|
||||
unicode-bidi: plaintext;
|
||||
}
|
||||
.excerpt-label {
|
||||
display: block;
|
||||
font-size: 9px;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.12em;
|
||||
color: var(--muted);
|
||||
font-weight: 700;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.meta-row { margin-top: -4px; }
|
||||
.reading-time {
|
||||
font-size: 10px;
|
||||
font-weight: 700;
|
||||
color: var(--muted);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.08em;
|
||||
}
|
||||
.preview-tags { justify-content: flex-start; border-top: none; margin-top: 0; padding-top: 0; }
|
||||
|
||||
html[dir="rtl"] .header,
|
||||
html[dir="rtl"] .header-right,
|
||||
html[dir="rtl"] .selection-head,
|
||||
html[dir="rtl"] .page-row {
|
||||
flex-direction: row-reverse;
|
||||
}
|
||||
html[dir="rtl"] .page-row .url {
|
||||
direction: ltr;
|
||||
text-align: left;
|
||||
}
|
||||
html[dir="rtl"] .notebook-select,
|
||||
html[dir="rtl"] .dropdown-item,
|
||||
html[dir="rtl"] .label,
|
||||
html[dir="rtl"] .sub {
|
||||
text-align: right;
|
||||
}
|
||||
60
memento-note/extension/dist-firefox/sidepanel.html
Normal file
60
memento-note/extension/dist-firefox/sidepanel.html
Normal file
@@ -0,0 +1,60 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>Memento Web Clipper</title>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=Vazirmatn:wght@400;600;700&display=swap" rel="stylesheet" />
|
||||
<link rel="stylesheet" href="sidepanel.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="app" class="shell">
|
||||
<header class="header">
|
||||
<div class="brand">
|
||||
<div class="brand-logo">M</div>
|
||||
<div class="brand-text">
|
||||
<span class="brand-name">Memento</span>
|
||||
<span class="brand-sub">Web Clipper</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="header-right">
|
||||
<div id="connBadge" class="conn-badge" hidden>
|
||||
<span class="conn-dot"></span>
|
||||
<span id="connLabel"></span>
|
||||
</div>
|
||||
<button type="button" id="settingsBtn" class="icon-btn" title="" aria-label="">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="3"/><path d="M12 1v2M12 21v2M4.22 4.22l1.42 1.42M18.36 18.36l1.42 1.42M1 12h2M21 12h2M4.22 19.78l1.42-1.42M18.36 5.64l1.42-1.42"/></svg>
|
||||
</button>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div id="settingsPanel" class="settings-panel" hidden>
|
||||
<label class="field">
|
||||
<span id="instanceUrlLabel"></span>
|
||||
<input id="baseUrl" type="text" spellcheck="false" placeholder="http://localhost:3000" />
|
||||
</label>
|
||||
<div class="preset-row">
|
||||
<button type="button" class="preset-btn" data-url="https://memento-note.com"></button>
|
||||
<button type="button" class="preset-btn" data-url="http://localhost:3000">localhost:3000</button>
|
||||
<button type="button" class="preset-btn" data-url="http://127.0.0.1:3000">127.0.0.1:3000</button>
|
||||
</div>
|
||||
<div class="settings-actions">
|
||||
<button type="button" id="applyInstanceBtn" class="btn btn-primary btn-sm"></button>
|
||||
<button type="button" id="openLoginBtn" class="btn btn-secondary btn-sm"></button>
|
||||
</div>
|
||||
<p class="settings-hint"></p>
|
||||
<p id="settingsStatus" class="settings-status" hidden></p>
|
||||
</div>
|
||||
|
||||
<main id="screen" class="main"></main>
|
||||
|
||||
<footer class="footer">
|
||||
<span class="footer-meta"></span>
|
||||
</footer>
|
||||
</div>
|
||||
<script src="i18n.js"></script>
|
||||
<script src="sidepanel.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
726
memento-note/extension/dist-firefox/sidepanel.js
Normal file
726
memento-note/extension/dist-firefox/sidepanel.js
Normal file
@@ -0,0 +1,726 @@
|
||||
/** Mettre à false pour le build Chrome Web Store (URL production en dur). */
|
||||
const ALLOW_INSTANCE_CONFIG = false
|
||||
const DEFAULT_BASE = 'https://memento-note.com'
|
||||
const STORAGE_KEYS = { baseUrl: 'memento_clipper_base_url', notebookId: 'memento_clipper_notebook_id' }
|
||||
|
||||
/** Compatibilité Chrome/Firefox : Firefox expose l'API sous `browser.*`,
|
||||
* Chrome sous `chrome.*`. On normalise pour utiliser `chrome.*` partout dans le code. */
|
||||
if (typeof chrome === 'undefined' && typeof browser !== 'undefined') {
|
||||
globalThis.chrome = browser
|
||||
}
|
||||
|
||||
let state = 'idle'
|
||||
let notebooks = []
|
||||
let selectedNotebookId = ''
|
||||
let pageUrl = ''
|
||||
let pageTitle = ''
|
||||
let pageDomain = ''
|
||||
let pageFavicon = ''
|
||||
let pageHtml = ''
|
||||
let pageDir = 'ltr'
|
||||
let pageLang = ''
|
||||
let selectionText = ''
|
||||
let pageRestricted = false
|
||||
let lastNoteId = ''
|
||||
let lastNoteUrl = ''
|
||||
let successTitle = ''
|
||||
let successTags = []
|
||||
let errorMessage = ''
|
||||
let activeTabId = null
|
||||
let pendingClipType = 'page'
|
||||
let analyzeResult = null
|
||||
let editableTitle = ''
|
||||
let connected = false
|
||||
|
||||
const els = {
|
||||
screen: document.getElementById('screen'),
|
||||
baseUrl: document.getElementById('baseUrl'),
|
||||
settingsPanel: document.getElementById('settingsPanel'),
|
||||
settingsBtn: document.getElementById('settingsBtn'),
|
||||
connBadge: document.getElementById('connBadge'),
|
||||
connLabel: document.getElementById('connLabel'),
|
||||
settingsStatus: document.getElementById('settingsStatus'),
|
||||
applyInstanceBtn: document.getElementById('applyInstanceBtn'),
|
||||
openLoginBtn: document.getElementById('openLoginBtn'),
|
||||
}
|
||||
|
||||
const ICON_SELECT =
|
||||
'<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2"><path d="M17 3a2.85 2.83 0 1 1 4 4L7.5 20.5 2 22l1.5-5.5Z"/><path d="m15 5 4 4"/></svg>'
|
||||
const ICON_CLIP =
|
||||
'<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2"><path d="M19 21l-7-5-7 5V5a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2z"/></svg>'
|
||||
const ICON_LINK =
|
||||
'<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg>'
|
||||
|
||||
function apiBase() {
|
||||
if (!ALLOW_INSTANCE_CONFIG) return DEFAULT_BASE
|
||||
return (els.baseUrl?.value || DEFAULT_BASE).replace(/\/$/, '')
|
||||
}
|
||||
|
||||
function isRestrictedUrl(url) {
|
||||
return !url || /^(chrome|chrome-extension|edge|about|moz-extension|devtools):/i.test(url)
|
||||
}
|
||||
|
||||
async function ensureApiPermission() {
|
||||
const origin = `${apiBase()}/*`
|
||||
const has = await chrome.permissions.contains({ origins: [origin] })
|
||||
if (!has) {
|
||||
const granted = await chrome.permissions.request({ origins: [origin] })
|
||||
if (!granted) throw new Error(t('errPermissionDenied'))
|
||||
}
|
||||
}
|
||||
|
||||
function escapeHtml(s) {
|
||||
return String(s)
|
||||
.replace(/&/g, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/"/g, '"')
|
||||
}
|
||||
|
||||
const RTL_CHAR = /[\u0590-\u05FF\u0600-\u06FF\u0700-\u074F\u0750-\u077F\u08A0-\u08FF\uFB50-\uFDFF\uFE70-\uFEFF]/
|
||||
const LTR_CHAR = /[A-Za-z0-9]/
|
||||
|
||||
function detectTextDirection(text) {
|
||||
const sample = String(text || '').replace(/\s+/g, '').slice(0, 3000)
|
||||
if (!sample) return 'ltr'
|
||||
let rtl = 0
|
||||
let ltr = 0
|
||||
for (const ch of sample) {
|
||||
if (RTL_CHAR.test(ch)) rtl++
|
||||
else if (LTR_CHAR.test(ch)) ltr++
|
||||
}
|
||||
if (rtl === 0) return 'ltr'
|
||||
return rtl >= ltr ? 'rtl' : 'ltr'
|
||||
}
|
||||
|
||||
function resolveUiDirection(text) {
|
||||
if (pageDir === 'rtl') return 'rtl'
|
||||
if (pageLang === 'fa' || pageLang === 'ar' || pageLang === 'he') return 'rtl'
|
||||
if (/\/persian\b|\/fa\b|bbc\.com\/persian/i.test(pageUrl)) return 'rtl'
|
||||
return detectTextDirection(text)
|
||||
}
|
||||
|
||||
function rtlAttrs(text) {
|
||||
if (resolveUiDirection(text) !== 'rtl') return ''
|
||||
const lang = pageLang && ['fa', 'ar', 'he'].includes(pageLang) ? ` lang="${pageLang}"` : ''
|
||||
return ` class="text-rtl" dir="rtl"${lang}`
|
||||
}
|
||||
|
||||
function sortNotebooksHierarchy(list) {
|
||||
const byParent = new Map()
|
||||
for (const n of list) {
|
||||
const pid = n.parentId || '__root__'
|
||||
if (!byParent.has(pid)) byParent.set(pid, [])
|
||||
byParent.get(pid).push(n)
|
||||
}
|
||||
for (const items of byParent.values()) {
|
||||
items.sort((a, b) => (a.name || '').localeCompare(b.name || '', uiLocaleTag()))
|
||||
}
|
||||
const out = []
|
||||
const seen = new Set()
|
||||
function walk(parentKey, depth) {
|
||||
for (const n of byParent.get(parentKey) || []) {
|
||||
if (seen.has(n.id)) continue
|
||||
seen.add(n.id)
|
||||
out.push({ ...n, depth })
|
||||
walk(n.id, depth + 1)
|
||||
}
|
||||
}
|
||||
walk('__root__', 0)
|
||||
for (const n of list) {
|
||||
if (!seen.has(n.id)) out.push({ ...n, depth: 0 })
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
function notebookSelectHtml() {
|
||||
const sorted = sortNotebooksHierarchy(notebooks)
|
||||
const opts = sorted
|
||||
.map((n) => {
|
||||
const indent = n.depth > 0 ? '\u00A0\u00A0'.repeat(n.depth) + '↳ ' : ''
|
||||
const sel = n.id === selectedNotebookId ? ' selected' : ''
|
||||
return `<option value="${escapeHtml(n.id)}"${sel}>${escapeHtml(indent + (n.name || t('notebookUnnamed')))}</option>`
|
||||
})
|
||||
.join('')
|
||||
return `<select id="notebookSelect" class="notebook-select" aria-label="${escapeHtml(t('destinationNotebook'))}">
|
||||
${notebooks.length ? opts : `<option value="">${escapeHtml(t('noNotebooks'))}</option>`}
|
||||
</select>`
|
||||
}
|
||||
|
||||
function formatReadingTime(minutes) {
|
||||
const m = Number(minutes) || 0
|
||||
if (m <= 0) return ''
|
||||
if (m === 1) return t('readingTimeOne')
|
||||
return t('readingTimeOther', String(m))
|
||||
}
|
||||
|
||||
async function getActiveTab() {
|
||||
const [tab] = await chrome.tabs.query({ active: true, currentWindow: true })
|
||||
return tab
|
||||
}
|
||||
|
||||
async function ensureContentScript(tabId) {
|
||||
try {
|
||||
const resp = await chrome.tabs.sendMessage(tabId, { type: 'PING' })
|
||||
if (resp?.ok) return true
|
||||
} catch {
|
||||
/* inject */
|
||||
}
|
||||
try {
|
||||
await chrome.scripting.executeScript({ target: { tabId }, files: ['content.js'] })
|
||||
return true
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
async function setPickModeOnTab(enabled) {
|
||||
if (!activeTabId || pageRestricted) return
|
||||
const ok = await ensureContentScript(activeTabId)
|
||||
if (!ok) return
|
||||
try {
|
||||
await chrome.tabs.sendMessage(activeTabId, { type: 'SET_PICK_MODE', enabled })
|
||||
} catch {
|
||||
/* ignore */
|
||||
}
|
||||
}
|
||||
|
||||
async function syncPickMode() {
|
||||
await setPickModeOnTab(state === 'idle' && !pageRestricted)
|
||||
}
|
||||
|
||||
function updateConnBadge() {
|
||||
if (!els.connBadge) return
|
||||
els.connBadge.hidden = !connected
|
||||
if (els.connLabel) els.connLabel.textContent = connected ? t('connected') : t('disconnected')
|
||||
}
|
||||
|
||||
function setSettingsStatus(msg, isError) {
|
||||
if (!els.settingsStatus) return
|
||||
els.settingsStatus.hidden = !msg
|
||||
els.settingsStatus.textContent = msg || ''
|
||||
els.settingsStatus.className = `settings-status${isError ? ' is-error' : ' is-ok'}`
|
||||
}
|
||||
|
||||
function applyInstanceConfigVisibility() {
|
||||
if (ALLOW_INSTANCE_CONFIG) return
|
||||
els.settingsPanel?.setAttribute('hidden', '')
|
||||
els.settingsBtn?.setAttribute('hidden', '')
|
||||
if (els.baseUrl) els.baseUrl.value = DEFAULT_BASE
|
||||
}
|
||||
|
||||
function selectionBlockHtml() {
|
||||
if (selectionText) {
|
||||
return `<div class="selection-panel has-text" id="selectionSlot">
|
||||
<div class="selection-head">
|
||||
<span class="status live"><span class="pulse-dot sky"></span> ${escapeHtml(t('selectionDetected'))}</span>
|
||||
<button type="button" class="clear-btn" id="clearSel">${escapeHtml(t('ignore'))}</button>
|
||||
</div>
|
||||
<div class="selection-body"${rtlAttrs(selectionText)}>「 ${escapeHtml(selectionText)} 」</div>
|
||||
</div>`
|
||||
}
|
||||
return `<div class="selection-hint" id="selectionSlot">
|
||||
<p>${escapeHtml(t('selectionHint'))}</p>
|
||||
</div>`
|
||||
}
|
||||
|
||||
function actionsBlockHtml() {
|
||||
const hasSel = Boolean(selectionText)
|
||||
return `<div class="actions" id="actionsSlot">
|
||||
${
|
||||
hasSel
|
||||
? `<button type="button" class="btn btn-sky" id="clipSelBtn">
|
||||
${ICON_SELECT} ${escapeHtml(t('clipSelection'))}
|
||||
</button>`
|
||||
: ''
|
||||
}
|
||||
<button type="button" class="btn ${hasSel ? 'btn-secondary' : 'btn-primary'}" id="clipPageBtn" ${pageRestricted ? 'disabled' : ''}>
|
||||
${ICON_CLIP} ${escapeHtml(t('clipPage'))}
|
||||
</button>
|
||||
<button type="button" class="btn-link link-only" id="clipLinkBtn" ${pageRestricted ? 'disabled' : ''}>
|
||||
${ICON_LINK} ${escapeHtml(t('saveLinkOnly'))}
|
||||
</button>
|
||||
</div>`
|
||||
}
|
||||
|
||||
function bindIdleHandlers() {
|
||||
document.getElementById('notebookSelect')?.addEventListener('change', async (e) => {
|
||||
selectedNotebookId = e.target.value || ''
|
||||
await chrome.storage.sync.set({ [STORAGE_KEYS.notebookId]: selectedNotebookId })
|
||||
})
|
||||
document.getElementById('clearSel')?.addEventListener('click', () => void clearSelection())
|
||||
document.getElementById('clipSelBtn')?.addEventListener('click', () => void runAnalyze('selection'))
|
||||
document.getElementById('clipPageBtn')?.addEventListener('click', () => void runAnalyze('page'))
|
||||
document.getElementById('clipLinkBtn')?.addEventListener('click', () => void runAnalyze('link'))
|
||||
|
||||
// Gérer l'erreur de chargement du favicon
|
||||
document.querySelector('.page-favicon')?.addEventListener('error', function() {
|
||||
const fallback = this.getAttribute('data-fallback')
|
||||
if (fallback && this.src !== fallback) {
|
||||
this.src = fallback
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
async function clearSelection() {
|
||||
selectionText = ''
|
||||
if (activeTabId) {
|
||||
try {
|
||||
await chrome.scripting.executeScript({
|
||||
target: { tabId: activeTabId },
|
||||
func: () => window.getSelection()?.removeAllRanges(),
|
||||
})
|
||||
} catch {
|
||||
/* ignore */
|
||||
}
|
||||
}
|
||||
updateSelectionUI()
|
||||
}
|
||||
|
||||
function updateSelectionUI() {
|
||||
const slot = document.getElementById('selectionSlot')
|
||||
const actions = document.getElementById('actionsSlot')
|
||||
if (!slot || !actions || state !== 'idle') {
|
||||
render()
|
||||
return
|
||||
}
|
||||
slot.outerHTML = selectionBlockHtml()
|
||||
actions.outerHTML = actionsBlockHtml()
|
||||
bindIdleHandlers()
|
||||
}
|
||||
|
||||
function applySelectionFromMessage(msg) {
|
||||
if (!msg || msg.url !== pageUrl) return
|
||||
selectionText = msg.text || ''
|
||||
if (msg.dir?.toLowerCase() === 'rtl') pageDir = 'rtl'
|
||||
if (msg.lang) pageLang = msg.lang
|
||||
if (state === 'idle') updateSelectionUI()
|
||||
}
|
||||
|
||||
async function refreshPageContext() {
|
||||
const tab = await getActiveTab()
|
||||
activeTabId = tab?.id ?? null
|
||||
pageRestricted = isRestrictedUrl(tab?.url)
|
||||
|
||||
if (!tab?.id || pageRestricted) {
|
||||
pageUrl = tab?.url || ''
|
||||
pageTitle = tab?.title || t('pageNotAccessible')
|
||||
selectionText = ''
|
||||
return
|
||||
}
|
||||
|
||||
pageUrl = tab.url
|
||||
pageTitle = tab.title || ''
|
||||
try {
|
||||
const u = new URL(pageUrl)
|
||||
pageDomain = u.hostname
|
||||
pageFavicon = `https://www.google.com/s2/favicons?domain=${u.hostname}&sz=32`
|
||||
} catch {
|
||||
pageDomain = pageUrl
|
||||
pageFavicon = 'https://www.google.com/s2/favicons?domain=google.com&sz=32'
|
||||
}
|
||||
|
||||
const ok = await ensureContentScript(tab.id)
|
||||
if (!ok) return
|
||||
|
||||
try {
|
||||
const ctx = await chrome.tabs.sendMessage(tab.id, { type: 'GET_CONTEXT' })
|
||||
pageHtml = ctx?.html || ''
|
||||
selectionText = ctx?.text || ''
|
||||
pageDir = ctx?.dir?.toLowerCase() === 'rtl' ? 'rtl' : 'ltr'
|
||||
pageLang = ctx?.lang || ''
|
||||
} catch {
|
||||
try {
|
||||
const [{ result }] = await chrome.scripting.executeScript({
|
||||
target: { tabId: tab.id },
|
||||
func: () => ({
|
||||
html: document.documentElement.outerHTML,
|
||||
text: window.getSelection()?.toString().trim() || '',
|
||||
dir: document.documentElement.getAttribute('dir') || '',
|
||||
lang: (document.documentElement.getAttribute('lang') || '').split('-')[0],
|
||||
}),
|
||||
})
|
||||
pageHtml = result?.html || ''
|
||||
selectionText = result?.text || ''
|
||||
pageDir = result?.dir?.toLowerCase() === 'rtl' ? 'rtl' : 'ltr'
|
||||
pageLang = result?.lang || ''
|
||||
} catch {
|
||||
/* ignore */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function loadSettings() {
|
||||
const stored = await chrome.storage.sync.get([STORAGE_KEYS.baseUrl, STORAGE_KEYS.notebookId])
|
||||
if (els.baseUrl) {
|
||||
els.baseUrl.value = ALLOW_INSTANCE_CONFIG
|
||||
? stored[STORAGE_KEYS.baseUrl] || DEFAULT_BASE
|
||||
: DEFAULT_BASE
|
||||
}
|
||||
await loadNotebooks(stored[STORAGE_KEYS.notebookId])
|
||||
}
|
||||
|
||||
async function loadNotebooks(preferredId) {
|
||||
try {
|
||||
await ensureApiPermission()
|
||||
const res = await fetch(`${apiBase()}/api/clip/notebooks`, { credentials: 'include' })
|
||||
if (!res.ok) {
|
||||
connected = false
|
||||
updateConnBadge()
|
||||
if (res.status === 401) {
|
||||
throw new Error(t('errLoginRequired'))
|
||||
}
|
||||
throw new Error(t('errLoadNotebooks'))
|
||||
}
|
||||
const data = await res.json()
|
||||
notebooks = data.notebooks || []
|
||||
selectedNotebookId =
|
||||
(preferredId && notebooks.some((n) => n.id === preferredId) ? preferredId : '') ||
|
||||
notebooks[0]?.id ||
|
||||
''
|
||||
connected = true
|
||||
updateConnBadge()
|
||||
errorMessage = ''
|
||||
setSettingsStatus(t('notebooksLoaded'), false)
|
||||
} catch (e) {
|
||||
notebooks = []
|
||||
connected = false
|
||||
updateConnBadge()
|
||||
errorMessage = e.message
|
||||
setSettingsStatus(e.message, true)
|
||||
}
|
||||
}
|
||||
|
||||
async function applyInstance() {
|
||||
const url = (els.baseUrl?.value || DEFAULT_BASE).replace(/\/$/, '')
|
||||
if (els.baseUrl) els.baseUrl.value = url
|
||||
await chrome.storage.sync.set({ [STORAGE_KEYS.baseUrl]: url })
|
||||
setSettingsStatus(t('connecting'), false)
|
||||
await loadNotebooks(selectedNotebookId)
|
||||
if (connected) {
|
||||
setSettingsStatus(t('connectedToUrl', url), false)
|
||||
}
|
||||
}
|
||||
|
||||
function renderIdle() {
|
||||
const restrictedBlock = pageRestricted
|
||||
? `<div class="restricted-note">${escapeHtml(t('restrictedPage'))}</div>`
|
||||
: ''
|
||||
|
||||
const authHint =
|
||||
!connected && errorMessage
|
||||
? `<div class="auth-hint">${escapeHtml(errorMessage)}</div>`
|
||||
: ''
|
||||
|
||||
els.screen.innerHTML = `
|
||||
${restrictedBlock}
|
||||
${authHint}
|
||||
|
||||
<div>
|
||||
<span class="label">${escapeHtml(t('destinationNotebook'))}</span>
|
||||
${notebookSelectHtml()}
|
||||
</div>
|
||||
|
||||
<div class="page-card">
|
||||
<span class="sub">${escapeHtml(t('activePage'))}</span>
|
||||
<div class="page-row">
|
||||
<img src="${escapeHtml(pageFavicon)}" alt="" class="page-favicon" data-fallback="https://www.google.com/s2/favicons?domain=google.com&sz=32" />
|
||||
<div class="page-text">
|
||||
<div class="title"${rtlAttrs(pageTitle)}>${escapeHtml(pageTitle || '—')}</div>
|
||||
<div class="url">${escapeHtml(pageUrl || '—')}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
${selectionBlockHtml()}
|
||||
${actionsBlockHtml()}
|
||||
`
|
||||
bindIdleHandlers()
|
||||
}
|
||||
|
||||
function renderLoading(label) {
|
||||
els.screen.innerHTML = `
|
||||
<div class="center-state">
|
||||
<div class="spinner-wrap">
|
||||
<div class="spinner-ring"></div>
|
||||
<div class="spinner"></div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="state-title">${escapeHtml(t('analyzingSource'))}</div>
|
||||
<div class="state-sub">${escapeHtml(label || t('statusAnalyzing'))}</div>
|
||||
<div class="state-detail">${escapeHtml(t('processingDetail'))}</div>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
}
|
||||
|
||||
function renderConfirm() {
|
||||
const excerpt = analyzeResult?.excerpt || ''
|
||||
const tags = analyzeResult?.tags || []
|
||||
const reading = formatReadingTime(analyzeResult?.readingTime)
|
||||
const tagsHtml = tags.map((t) => `<span class="tag-chip">${escapeHtml(t)}</span>`).join('')
|
||||
|
||||
els.screen.innerHTML = `
|
||||
<div class="confirm-panel">
|
||||
<span class="label">${escapeHtml(t('previewBeforeSave'))}</span>
|
||||
<label class="field">
|
||||
<span>${escapeHtml(t('noteTitleLabel'))}</span>
|
||||
<input id="titleInput" type="text" value="${escapeHtml(editableTitle)}" maxlength="300" />
|
||||
</label>
|
||||
${
|
||||
reading
|
||||
? `<div class="meta-row"><span class="reading-time">${escapeHtml(reading)}</span></div>`
|
||||
: ''
|
||||
}
|
||||
${
|
||||
analyzeResult?.summary
|
||||
? `<p class="summary-preview"${rtlAttrs(analyzeResult.summary)}>${escapeHtml(analyzeResult.summary)}</p>`
|
||||
: ''
|
||||
}
|
||||
${
|
||||
excerpt && pendingClipType !== 'link'
|
||||
? `<div class="excerpt-preview"${rtlAttrs(excerpt)}>
|
||||
<span class="excerpt-label">${escapeHtml(t('excerptLabel'))}</span>
|
||||
${escapeHtml(excerpt)}
|
||||
</div>`
|
||||
: ''
|
||||
}
|
||||
${tagsHtml ? `<div class="tags preview-tags">${tagsHtml}</div>` : ''}
|
||||
</div>
|
||||
<div class="actions">
|
||||
<button type="button" class="btn btn-primary" id="saveBtn">${escapeHtml(t('saveToMomento'))}</button>
|
||||
<button type="button" class="btn-link" id="cancelConfirmBtn">${escapeHtml(t('back'))}</button>
|
||||
</div>
|
||||
`
|
||||
|
||||
document.getElementById('titleInput')?.addEventListener('input', (e) => {
|
||||
editableTitle = e.target.value
|
||||
})
|
||||
document.getElementById('saveBtn')?.addEventListener('click', () => void runSave())
|
||||
document.getElementById('cancelConfirmBtn')?.addEventListener('click', async () => {
|
||||
state = 'idle'
|
||||
analyzeResult = null
|
||||
await syncPickMode()
|
||||
render()
|
||||
})
|
||||
}
|
||||
|
||||
function renderSuccess() {
|
||||
const nb = notebooks.find((n) => n.id === selectedNotebookId)
|
||||
const tagsHtml = successTags.map((t) => `<span class="tag-chip">${escapeHtml(t)}</span>`).join('')
|
||||
const reading = formatReadingTime(analyzeResult?.readingTime)
|
||||
|
||||
els.screen.innerHTML = `
|
||||
<div class="center-state" style="justify-content:flex-start;padding-top:12px">
|
||||
<div class="success-icon">✓</div>
|
||||
<div>
|
||||
<span class="badge-ok">${escapeHtml(t('noteSaved'))}</span>
|
||||
<div class="note-title"${rtlAttrs(successTitle)}>${escapeHtml(successTitle)}</div>
|
||||
<div class="state-detail">${escapeHtml(t('sentToNotebook', nb?.name || ''))}</div>
|
||||
${reading ? `<div class="state-detail">${escapeHtml(reading)}</div>` : ''}
|
||||
</div>
|
||||
${tagsHtml ? `<div class="tags">${tagsHtml}</div>` : ''}
|
||||
</div>
|
||||
<div class="actions">
|
||||
<button type="button" class="btn btn-primary" id="viewBtn">${escapeHtml(t('viewInMomento'))} ↗</button>
|
||||
<button type="button" class="btn-link" id="againBtn">${escapeHtml(t('clipAnother'))}</button>
|
||||
</div>
|
||||
`
|
||||
document.getElementById('viewBtn')?.addEventListener('click', () => {
|
||||
if (lastNoteUrl) chrome.tabs.create({ url: `${apiBase()}${lastNoteUrl}` })
|
||||
})
|
||||
document.getElementById('againBtn')?.addEventListener('click', async () => {
|
||||
state = 'idle'
|
||||
analyzeResult = null
|
||||
await refreshPageContext()
|
||||
await syncPickMode()
|
||||
render()
|
||||
})
|
||||
}
|
||||
|
||||
function renderError() {
|
||||
els.screen.innerHTML = `
|
||||
<div class="center-state">
|
||||
<div class="error-icon">!</div>
|
||||
<div>
|
||||
<div class="state-title" style="color:#ef4444">${escapeHtml(t('failure'))}</div>
|
||||
<div class="state-detail">${escapeHtml(errorMessage || t('genericError'))}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="actions">
|
||||
<button type="button" class="btn btn-danger" id="retryBtn">${escapeHtml(t('retry'))}</button>
|
||||
<button type="button" class="btn-link" id="backIdleBtn">${escapeHtml(t('back'))}</button>
|
||||
</div>
|
||||
`
|
||||
document.getElementById('retryBtn')?.addEventListener('click', () => {
|
||||
if (analyzeResult) void runSave()
|
||||
else void runAnalyze(pendingClipType)
|
||||
})
|
||||
document.getElementById('backIdleBtn')?.addEventListener('click', async () => {
|
||||
state = 'idle'
|
||||
errorMessage = ''
|
||||
analyzeResult = null
|
||||
await refreshPageContext()
|
||||
await syncPickMode()
|
||||
render()
|
||||
})
|
||||
}
|
||||
|
||||
function render() {
|
||||
if (state === 'loading' || state === 'saving') {
|
||||
return renderLoading(state === 'saving' ? t('statusSaving') : t('statusAnalyzing'))
|
||||
}
|
||||
if (state === 'confirm') return renderConfirm()
|
||||
if (state === 'success') return renderSuccess()
|
||||
if (state === 'error') return renderError()
|
||||
renderIdle()
|
||||
}
|
||||
|
||||
async function runAnalyze(type) {
|
||||
pendingClipType = type
|
||||
state = 'loading'
|
||||
await setPickModeOnTab(false)
|
||||
render()
|
||||
try {
|
||||
await ensureApiPermission()
|
||||
await chrome.storage.sync.set({
|
||||
[STORAGE_KEYS.baseUrl]: apiBase(),
|
||||
[STORAGE_KEYS.notebookId]: selectedNotebookId,
|
||||
})
|
||||
|
||||
if (type === 'selection') {
|
||||
if (!selectionText) throw new Error(t('errNoSelection'))
|
||||
await refreshPageContext()
|
||||
}
|
||||
|
||||
let analyzeBody
|
||||
if (type === 'link') {
|
||||
analyzeBody = { url: pageUrl, title: pageTitle, mode: 'link' }
|
||||
} else if (type === 'selection' && selectionText) {
|
||||
analyzeBody = { url: pageUrl, title: pageTitle, mode: 'selection', selection: selectionText }
|
||||
} else {
|
||||
analyzeBody = { url: pageUrl, html: pageHtml, title: pageTitle, mode: 'article' }
|
||||
}
|
||||
|
||||
const analyzeRes = await fetch(`${apiBase()}/api/clip/analyze`, {
|
||||
method: 'POST',
|
||||
credentials: 'include',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(analyzeBody),
|
||||
})
|
||||
const analysis = await analyzeRes.json()
|
||||
if (!analyzeRes.ok) throw new Error(analysis.error || t('errAnalyzeFailed'))
|
||||
|
||||
analyzeResult = analysis
|
||||
editableTitle = analysis.title || pageTitle || pageDomain
|
||||
state = 'confirm'
|
||||
render()
|
||||
} catch (e) {
|
||||
errorMessage = e.message || t('errNetwork')
|
||||
state = 'error'
|
||||
render()
|
||||
}
|
||||
}
|
||||
|
||||
async function runSave() {
|
||||
if (!analyzeResult) return
|
||||
state = 'saving'
|
||||
render()
|
||||
try {
|
||||
const title = (editableTitle || analyzeResult.title || pageTitle || pageDomain).trim()
|
||||
const saveRes = await fetch(`${apiBase()}/api/clip/save`, {
|
||||
method: 'POST',
|
||||
credentials: 'include',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
url: pageUrl,
|
||||
title,
|
||||
content: analyzeResult.content,
|
||||
summary: analyzeResult.summary,
|
||||
tags: analyzeResult.tags || [],
|
||||
notebookId: selectedNotebookId || undefined,
|
||||
}),
|
||||
})
|
||||
const saved = await saveRes.json()
|
||||
if (!saveRes.ok) throw new Error(saved.error || t('errSaveFailed'))
|
||||
|
||||
successTitle = title
|
||||
successTags = analyzeResult.tags || []
|
||||
lastNoteId = saved.noteId
|
||||
lastNoteUrl = saved.noteUrl
|
||||
state = 'success'
|
||||
render()
|
||||
} catch (e) {
|
||||
errorMessage = e.message || t('errNetwork')
|
||||
state = 'error'
|
||||
render()
|
||||
}
|
||||
}
|
||||
|
||||
chrome.runtime.onMessage.addListener((msg) => {
|
||||
if (msg?.type === 'SELECTION_CHANGED') applySelectionFromMessage(msg)
|
||||
})
|
||||
|
||||
chrome.tabs.onActivated.addListener(async () => {
|
||||
if (state !== 'idle') return
|
||||
await refreshPageContext()
|
||||
await syncPickMode()
|
||||
render()
|
||||
})
|
||||
|
||||
chrome.tabs.onUpdated.addListener(async (tabId, info) => {
|
||||
if (info.status !== 'complete' || state !== 'idle') return
|
||||
const tab = await getActiveTab()
|
||||
if (tab?.id === tabId) {
|
||||
await refreshPageContext()
|
||||
await syncPickMode()
|
||||
render()
|
||||
}
|
||||
})
|
||||
|
||||
els.settingsBtn?.addEventListener('click', () => {
|
||||
if (!ALLOW_INSTANCE_CONFIG) return
|
||||
els.settingsPanel.hidden = !els.settingsPanel.hidden
|
||||
})
|
||||
|
||||
document.querySelectorAll('.preset-btn').forEach((btn) => {
|
||||
btn.addEventListener('click', () => {
|
||||
const url = btn.getAttribute('data-url')
|
||||
if (url && els.baseUrl) els.baseUrl.value = url
|
||||
})
|
||||
})
|
||||
|
||||
els.applyInstanceBtn?.addEventListener('click', () => void applyInstance())
|
||||
els.openLoginBtn?.addEventListener('click', () => {
|
||||
chrome.tabs.create({ url: apiBase() })
|
||||
})
|
||||
|
||||
document.addEventListener('visibilitychange', async () => {
|
||||
if (document.visibilityState === 'visible') {
|
||||
if (state === 'idle') {
|
||||
await refreshPageContext()
|
||||
await syncPickMode()
|
||||
render()
|
||||
}
|
||||
} else if (document.visibilityState === 'hidden') {
|
||||
// Désactiver le pick mode quand le sidepanel est fermé
|
||||
await setPickModeOnTab(false)
|
||||
}
|
||||
})
|
||||
|
||||
document.addEventListener('DOMContentLoaded', async () => {
|
||||
applyDocumentLocale()
|
||||
applyInstanceConfigVisibility()
|
||||
applyShellI18n()
|
||||
await loadSettings()
|
||||
try {
|
||||
await ensureApiPermission()
|
||||
} catch (e) {
|
||||
errorMessage = e.message
|
||||
connected = false
|
||||
updateConnBadge()
|
||||
}
|
||||
await refreshPageContext()
|
||||
await syncPickMode()
|
||||
render()
|
||||
})
|
||||
@@ -6,191 +6,289 @@ const fs = require('fs')
|
||||
const path = require('path')
|
||||
const zlib = require('zlib')
|
||||
|
||||
const VERSION = "0.3.1"
|
||||
const VERSION = "0.4.0"
|
||||
|
||||
// Embedded Momento clipper/extension string table (gzip+base64).
|
||||
// Source English + contextual French anchors; other locales are MT batch
|
||||
// post-edited with placeholder hardening.
|
||||
|
||||
const PACK_B64 = `
|
||||
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
|
||||
wOW7jPkAAA==
|
||||
H4sIAAAAAAAAA9192XMUR9bvv5JBTITni2CJebgvxMREYMx84zse4ANsx/VbqjtpFaqu6qmqlixN
|
||||
+At1a2skYTBGbJaMBC20WPuCGiFExNfw3v1WjV8UbrRZTcy/cO85mbX0UosYw0TcFyypsrIyz/I7
|
||||
53dOZfkfR5hy5OQ/jrCvjbM0zuDHONN1GmNHTh75G4szxVDJl6yJnJalRIJpR745CmM/YXpEkxKG
|
||||
pCrVt5ymCSOpMdLGmkiCxphOqBIlzVKsWZZizQaLEoN9bRAJpm1XkxqxnqGoBmtS1Rad/NJ5i0RU
|
||||
RWERQyfWMLVNsYfqTGtl2nGxlFMRWMUlyZBrVg8rhvvFbTC8jTVZ+6gaWrNB8XQWrZnQ/vM3R49E
|
||||
Jd1j2FnVIBH3UEnRDapE2EVmGJIS0xsL+fMLn7kHf67Jn9EmJlcP/j9ukVlDrVsTGtOZcV5To8lI
|
||||
vWacvxM+kPzPGonzqY6B9I9H1DhMQxMJuf0CE1uonuQUXEOVavaAb44eURNM+ZvKxVw1/lyCKW4F
|
||||
6EIEf5GUmpnPU91gxGhm5C+XLp2/SH6vauSzU2f/AzZH1MvVxiIsgJxW1RaJ6URSiNEs6aRJU9t0
|
||||
ppFmqkRlRnQpphyTFLSUy6pqMO0Lpul1kmlg5uQfrXzkN2hlmnaeaXFJh798whSpVuXWDBGqfGQQ
|
||||
GokwXecrMmjTcXK6mUVaSAtrb1KpFj2hSwYj7GuDKTpqw56a276qEZAnyuKiFGXkPFWYjJuwnORz
|
||||
RaHx2kV8rhjgBFHblfgdZy3HqrVSl8u1M1SjxmhUUmKXpDg7p9R403//gcQlJWkwAqNqRxvNtS51
|
||||
KpHQ1K+Pk9+dPvf52Uu/g5v5nUePJGQaYc2qHGUaLgpHwA8RVTEYGMaR3/3hCDg3jSfAq4/8ryPf
|
||||
fIPWIzO04E+Y0cDxLlqXSdS6Dg4VU1StZjPib+4p6y3ykpQ46SAXxy2VqwWQDaAlAhBDwZ8iks6I
|
||||
PRmhOqEoX1QbDLMX1wCk7PuswefxYj2YgUXBs3HltJV9Jikt5xS5vUYOtJURWVJaiArXABlojJ1V
|
||||
jVNomFJTHVKi2eJmmpnG0ArtZ5EmWY206C6D5fZ9XHjGZ2pMUi6wvyclrVYh52VGde6G6KKqC7qq
|
||||
HfaypOmGMyONeljtaTUpo4ETWaWOpevHySWt3cEkSYlV+YsOUzbAaWH+Mr/qIL+kxBpCv6TEful8
|
||||
XBUiLqmfa7JHnIAd/+7zC5/9roHRA2b7mXyzYST0kydO1CP0N+h7uqFJ8Ix6U7lgX+P6w3gKum2V
|
||||
KFqvpQRDVeUmqgHg1OBMlOmGpFAwSUtK1c/4xBlQBTc0YkitrH5Np/Dvtu0mNNYqsbaP2WVVY2Cu
|
||||
tTuAq6QJLxOdtoI+hDox0DeIjDwBwJQgwrSE0WDIGX7Fcp5LasOQhd5TnTY00UjN/j+Gv8B+FSq3
|
||||
d0hK7KKa1CK1m7YuEp1fhQcb1Ejq9hWPG4SR8cEX+f5rF+kMS2gq+rUS+4QZVKrZ9X8yhWkUTJcY
|
||||
NKYfJZToLE4VQ4oQPRmPU639KEZ0Fm9iUUB03fYdkEUDrwGVcH/RmWJcUhvbCN6MLnD23KUzH587
|
||||
99dGfmBd83WGC4xGiUwNSM/A/ME8Pm2ccXwBliNVJR1g/KcUtT5GIahSfsW2zctUkpO1AcPBnYgK
|
||||
azJwaAxEK0XOaJpaM/NFNc6MZpB5G1MM0qapSgzCXwT/1t4oizvOY6qhtdc6A/yJI+NZ1SOM8D/z
|
||||
GIVYehS8OsKDBiOXk7KMG7Qgllsa+zOV5Pos19oqN27mhALrbtCs/61gIHybdhCE5TOjTdVqzET8
|
||||
kUi6nhRghekS3m3hMUQdJUpcufJx7piKwrTzUqTlEvu6Jnz/xTNyV8umrVmVmbU90L8WyIf+Z+0w
|
||||
lOiMorGYpBsa6yBRpgtaBASJGSSaxOUxoic1WYop+SkSpYpOWlWdRKimMEO3d+0iRvkpkh8lraqh
|
||||
MZLQ1ATAJOTEjl2FokeQ6UKiG4YjXVKVKEvqDLYeTJLyU0EkSXH2EkySTkU1puvMvU5/ogSsIcqE
|
||||
hGyi5Lo9iCzlp7T8VEyGAJpwmNM7Uibp70mmgbrtDIWbjTdvSrZqkhaOOZ1WZZl1EPkj2LNFnpKc
|
||||
PNkiqLUO8hnTSUTQpygjEUYU2irFqAGjYvlJDXBLplxJX0vqB2RRCiMJljRIArLoSCQ/FWUamHuE
|
||||
gRvHZGYcJ1/kpzTpsgT7hhpD0lA1Scd8RAefisi0VWLaCSdpjUIeagDAfE/UJFGTrRreTBKAITQJ
|
||||
4SU/pdEQROs0+iXRwU8NyeBEwpNonac6YUoEMhmQM/fpILb1y9UMsQlXlBFA9yR/kD/tghtdlCtp
|
||||
cPVa979H8pWfstlXfspAv2b+/EsLIGCndCMZYeSkhY2AnxZc5keJ/EvnvfxURKMKSWCsEEaXVBjR
|
||||
ndUktPwUsjMQA+UpMbBrqhjk70kWmqXB1DJ1zexP1mB4hBkGC0HYnAihgUXKElNIUgHQAIcIQeA+
|
||||
jSdU/nfYZTQ/FVGTsAApIv3P6EnXOoDMwablj8CxJsFzvnaYXRhO9wVLSog30Y9ok6pFSaua1O16
|
||||
F/qp5ccYyKqAJQTDq95KpJlqMZSKjjxUR9jVj5Mzuk7bMaDyBbiANZj5nRaBFWfPT+mBzA/wLzzx
|
||||
48H5wxM/+AvBAUxSDIaat3ifTEkT1QCDPlKThiQ7WYWa9ETBQBookBBszhnqxwNxifxiCB5ogbxG
|
||||
aCu4a/Qj5nhKCEKoBRJCQ6NSECF0eyeatC8z/ETVwxFDG5EOQw51pgVyw/zViKqocYmFo4f5KQU0
|
||||
znPspKHGqQHIg6kqJ4xafkpPxvNTgH1IHOEyM0iEypGkjAPDcccLLE41vNkSqYgRfjTyjDOWi//D
|
||||
0klVqte6L6G0QhBNYnLuxyqrsc6A5Ejhhu3NLD9XGGGaBlka0w0Ija1MAURXNV0YVESNx5OKFBE6
|
||||
bWURjzzYk3LmpxjiqxbIO+1oiNH5l857PCjITMRpNUkwFnW4Yq8hQXoZgoq65fMRFfbvimZBfLTq
|
||||
fhd0CHEEUtPzmtok5yfjqB7wAkaTgKqtdvLJJ7JzZPAKnoe7JezHUv8Gm+mAhERPak2aJMuoIFt+
|
||||
elKzJecrS4Tr37KTd0a7THWdKeSihJxPZ5KBeUkUisStqhZjzWoTU5hCLvGWHvm0WXPaPcfOqobU
|
||||
0ZTfiDQzTcG8WzeYLBuESQojXzCtSVKiSSVGOpJ4Z5wwKYbzWTNcxDYPPC5cs48mL/923T5YYFKJ
|
||||
MiWAyEqRZgNItDM4VL/vWKh+H8jFFsenOLTjWPh2XwsSolYVEgIUPoj7XTis0sZgd6h9pinAzlq5
|
||||
AvmWPYls/vbly4owolBs9s/5jZgYH5VEM/AY+b0KHPCzU2eP/Qd4F4hFrzETHeyqqiUYlZjO4uRj
|
||||
0WNoZRrVmrgVw9SnlDiTwQA/HLNtoYpCFLQYsFVcILmAsMS0FqoZjHQkYxqTLrP/t5X8cBPTElp+
|
||||
47JLIJcoRn7t2ImLksGOndHaYEdaUonpTUxjkWZDiiUVECEyXaTOTxwdgBIvogxgtBRpDqa73I+T
|
||||
kWaiNiuMXJIMJge0FiPNpAW93I0BITqMf+OEN8ZkpnPD8me7p2lVg/G4fef747mnknobbZYJ6Evh
|
||||
5MyT4UpM47vw7zImTpK/Ua0FR6OSEE/RQJjGtXWUJOMcNxNaPtsBdNZaCJV1LmZCk3pHUo80K0yK
|
||||
MiUMpz0mJgnoO3I7xYXAQ/zJ7NmkRuAK0RNgX0xTQhDYv0CSj94BdoNeRpgm4kUb06LCnt0rwb6k
|
||||
xDQDjfqrZEyTLl+2/Iq4HCMEq/1YgpwC8ECoQJcizaQjqeSzkWbdaIAmTUyyKRRVQhBbp93Yoipg
|
||||
ggIIYkym8FS+y+MQGPVkpJmv4yjuxRUr2yQWZVoz0zqSKB2h56BGp+2E1uP8KO8XFrKHoLx2lCRx
|
||||
yfg3cN4zkhJjeqRZy2eVFsMyDTCVU0nd9gWS32gC9uYEoWMX2+NNUDgF6OUw2RgaAznwVxKTFQsi
|
||||
fXugLdgDxUeEIb8qLClOLlpuxDch4kEI9stR2pv9ngK4iAWQ309dr125/bme+H6V1PIbIbqi/5UE
|
||||
s+XNJd1ByGDqi2ODO6MJ6gwL0RnlqNuqKuQSkl0AWc3qkOroh18ldRqPMwXyYfBBSILOSEoTMwyM
|
||||
tAHtUqmDxJglPCOI7v6nM5QkFaiofVC+69I3VXTWzOwyZ0PCe9H2MABNDFAcdmvCRUP6+1fEQSsf
|
||||
akI/llXkHBwM/Znwx0yKkzMauirj5EOrzZWJpCPfIH9mzTLUkZKXY8zQmCG01oD8fskRVpX58/3Y
|
||||
75f5bLMs9t6RhHiFICLCt85hRa8SEUBQjOk0brgkFIIKf+IKfC1VcrN8w4qTgaQYKAU3y6qJXDZa
|
||||
M5NX27ajjWktCeDILI6Q6wYo3C0+yxW9kk4P91iYHm5QWnQYCcNedE+CfFrVDBpVNYqNWRdTCfOy
|
||||
ayI/FpMUypu67cjcVSjEUhnm5OReJ5EkjTJNUW3icpLoWDtgEYPCWxlJbOVK/PVGKaoerpdLqEzi
|
||||
aiD5rdqofx8X1hVVg/q41g6iVV3ZxtyXd2Wr1unPfz+jRHRyjaRVvpIo/H6IVi6jVyBZgrt4Kzci
|
||||
5ZffsZcrSxGqEfGqGWzbv49rY2kTeGjQK7AslsQCnruRa/dx9WS9XZDPqNPFZQpUBLHdwmKgYtCM
|
||||
oakyVQiTiaRIEUnFmZgOAngX2vvHP/7xizMXLn6a//7sn/70p8N1dVWSSLIow9cHwW0pLJeSBPyb
|
||||
X6SY+UqXsbgsq7p4LVbFoqboUIHWwHx0yZDUEwaLyOBeIF2NwhbxBTYe6EL1cYVDEh1eR8zPGklZ
|
||||
9SW2p/Lr8LobaabtjjeHoLXYiFWdPiwNZran+Nuzf/zjH0+fO3vp1IU//elP1s0knp9VpDglv/d6
|
||||
x/Y/3vdLthGhCgMdn/qSYBrU5j2tKjq7op4UiMlAkxxCmYKVTo6uJEE1in6nGRRK7JS/N8vXwl/B
|
||||
xdo3vthMg+hv1T6wT6Z7s+AL1lO5xfIF+dPg/0xSLQp3KKAIoqsyBT9gIcjwWYgAwlns/dK/J/Oz
|
||||
J6sWIJq5aPr4Tq5KsNHneEuYd3Q1Kc40lcODjQ3EqcLW40oossu3EJWYBm/6UC1GNXRr22+Ok0/R
|
||||
EKGBK7ciHliQqrMQPVw7muLkUTWoiwszK1E1XBuXBzRC/x1dXKFcPkaJSVF6UpgBVCiiEgWZiX4u
|
||||
hsJmpmk0LjHFoLorNBIVDCNRh4nBXV0LF+2+rurb0xULxus0BLOF13t1MGlFvJ0S474SyGkdiA7o
|
||||
6UZEqPXmtY57upORelZ7ytDyY+E6ulIH2Be5nLTcPJDT8jsCKe2pZlXT1EO87GvDGsNerUGhicv0
|
||||
ZBwyURbPj8HLvyqBhmBES+oGjUiqwoJe+6VCTxzvfTksH4fQ8WG7taxWo57M1YZ01dCqIL0hVbXx
|
||||
TLVeAKYBfdpTckwlOpWl/DKJUyjTEllmgILUK5f1fgO4NUQnlsczVXHFz4TAdtXGD1c4tfYRgny6
|
||||
dk+52VZHwiDK6bpfuDqIwI7Svu1XFkcRaSx6Ejo4IkHUkxgsvkZDb7dy5hpR+jHLC7W5RrRKOI7E
|
||||
DDVKXVc4kZSM37LTepoamNclaIwXUJoItH91iL2twGs7JIqvRUrESKoSMWgkkpQUycUjJd6DhiI2
|
||||
lWGUOCJ3OAapJ0O9CExjMiwIXiaSyGUVXr2Enrw4UeXJJ9H8jUA+Kd4L1vX3wic/ErfIH0lYIKKw
|
||||
h8NwSQneKTBoB3paVOKMkv/2ri8HR6jGCCOaZOlQ06VwrDIRTCo/VSKqLNOa14MtVtnQVsinglNC
|
||||
2+PvSTRE66BWDPIBUJ7K3yEEPf0rRPLc2TOHJ5IKSSTzSxaRZITyVVKoBLEohSYwEl/YNhMvB3d0
|
||||
oM4k4JXEoLohMY2egMQWX31kFrFUiZpIQFGHJjSJSDJ/M06WVSuHYsHE8hJ3UBAsWJghGWoAszzL
|
||||
oLRseTbAbEStp4k+1FIiMjPCUctIddMUb1TfM2PkDqJJMmulRhBfDDqYeTEZizEIbYh9NkSCsjhq
|
||||
6klZFnBKUd+aZCBoaczmjXxFLtrIQtNGfisM8mGM4omWZfLF+FPGi1RuhdcBZRW2EuGA6SBTuFeA
|
||||
JZm5t/v3pHSyeg3YN41Qx30JleWPHEcIwRlhDQh9UIWg7kOd1WhxiNd9YdkRqnEsdELccXJeU1vh
|
||||
IS50NKRgenjJipF8UkMKYIcQcGD3Yd/yRbn9e97xBR3KUlwyqAHM0DI0AzhgNTOMyRLRDQ3yfkNy
|
||||
0UJPVAukhjayRSXrlV/0Bl9+yJdMjbD0UGp1rCsqQRLdSq1DFr4dTzWQHOqGRo1AcigcsSobqqeG
|
||||
nypRiRmaGo4c6hKmHZRcVsPTQ+SHx48f9+eHFyQ9QbW4dBiKaOcvBo0dJZrEEpKsAmMRR0MFO1S1
|
||||
hKohBkmB7BD1ZASSQxQuR4sPfDBUT3KZhj0eamN4UvmIyobmRnHf93kRzCyaGHBS9L+SVI6oOiX5
|
||||
STjiCJLRDVXDfF8mGo3FJGgya+jYRpISK28NIo1SApAzDG0EW6DC3+wYqjrIIn8Exwns7R/ixV1J
|
||||
NPpB6hiMXHEo7Mu7MIeAABDBIVijRKDRe9Jqg8jUPs/GsZ6wQ76seyYg1fCVGTjmb0wba7qPUWpz
|
||||
R85oobKDZZg40VmSn6DhDRU49EzzE/lV5j5e+r2owLJjOiMUwABakvnlhPauTUnGq/YhWKVFDKI0
|
||||
+IBpqMZk/tHhW5NqeCaJ4yFIUKzl5B/yYo56KDIZZZclRcpPwFqt7iT/7V9oTrLDNyeDaeRpOCSt
|
||||
kkZHTLmd1PcmzzmtScWjMRknKpEhz/wXKGS+69yhGSRIOKFiLxIuimpWLClVdSGp1YTU0U/sPqTQ
|
||||
lug/nnAfK8VOpEoSVDpcJ9JqRLJ4uEakpEQp30ZzfoxE3q0VKR2qFVl1qlTV3VO8X+5oucdv0mz8
|
||||
RIrQkxwaQcWqwErFs80Y53SRL6KqywiXwlJG28VhHAtqNLJD9BkxndKgFAQhAL76E6bDCKvJTxGI
|
||||
sPnZVkg0XH1GCa25QadRAtumnDTmRx1vCEEZ/0zzE5T7OnTQnaPe1cCAFWtJU8O0GGEPl1XJtYkI
|
||||
nHCEEruqV30aCLuMDiiGOSDq9BY19v9vd9GgqGqr1p1QNRJn8JKIq7t4GbuLornoaE5Nwm8NgC7E
|
||||
odF36i4eprnInN4iTx1/k9biJTheEYo7ai4bb0wev1BlEZ5DUEfeVwzPG/VQbUVcqTMwiDVq1AYx
|
||||
cSwUOooYs/IP6ygjzy3DccYwjBGT2A/fS4yHI4p2LzEZqpnYALlUJSInpTA9xShLwgFQBBAVvpUD
|
||||
gJcf9c5EvRgiwCJaaqvzZkpghxF5k+ruL0oagoGFIU7MAAIkaaH6i/UCodyOD9lnrJ+Huz8K57D9
|
||||
Rp7UuXuO/A2Vr+EpjAjCEJY7fuKTcgSID3xIDsMcI+GI42cMPkvVxJnpR/jiYAxOZrcweO2XGKxF
|
||||
N0gr5cdekm32G7vO9yKPkTilLfYZQPimVZwZMBaPcNp3HOZLrQqlWhiiWL1PD/Z3isKxEFk1go9v
|
||||
Mr4N9T2c3vzckR0faEjsEKc3I4bEWlVVE2c3pXc7u3lJZQl+kJcpRE0oEku2hTy8acf6BBzL9SeI
|
||||
52XaAo5jscPLztdbW2mVGTX4eGuUdTC7xdgBR4vhSIYKR3/BCGU1Zh2zeMc246GbjM2MXYZ34uFL
|
||||
MSqLUfh6nmqQqGTAJ12bZBq1u4wMzw4RPHtprdtQmaEzBc7CH0OCeEy8lMfEN14v82+8wv46pCvw
|
||||
klaYT7yepTQuqzy7lQyJNams5Yr/54fOqjG+japbwlLFpEFk1hHmKOY5JcZaGejV3VuE85gd7/U8
|
||||
pghW8M6/+Oor0zy/+6qwWLgjmSeJwEOBhmoC6zOizBcnDM8jDvzSOYCUEMxXtxYCBzKFsAmc1lDA
|
||||
FEMdx7Sm8OaHf1XgOCY8Tqwl6Xy+pDE/PCXLsFj+OdiELlMa5jzmX6lCmuFMpgL4KLZwsurJTbLa
|
||||
AhLCz7+4fcS29BC08G9MjpIkYXh2hlKFNElX3P1ENzCE4ISw7Go75xvAI4/YS2wCndagIH4ak5E4
|
||||
bQl7mtI1/3s4T6ny85Ts33Ge8mOWYFqLYWu56uOxzvtVx+DoTxOVWwDJ6kEskAN+DLw/HodQUgdl
|
||||
ft+QZa3M1YQJoIIufAZQjlKDJIUPGCG6iUHnJz+XDENjLTof50MJz3G3q2k71XPCC/w0pi8h/FjD
|
||||
T2HyT6GEPjoZ6uTkxwzaiGE/KytOS/I3Sq2zkkQHKtFKMUmAfENSdDkpwW+YZCpMg6UEHZgE5FQT
|
||||
kMDRmIXW3vTwnD3yg/cUP2Yt0pWWsP1EBG9FjaHUqvHb63wkh69WVTZUVQo6EHlGI5JOJPiyWVzS
|
||||
YyxmASo4KJ5pboH8pDobsxNTL5p4ToAlHPSzTC74U7TgdBzVRQC9jFGEf69NlllUirljWAiSCNju
|
||||
jj8oGccTgrjhX/m2rcCMd4t4GPh5WvuYI4vb3UTYYHUIcfGesGcbvRIMR1jNTK6RE3imNxs8S7WO
|
||||
l/eiHXAQEjrJWkf7qw2mSJToEotUveroRxHPax0s0tzWbiSvwCSq0o58FL72prbBCzodtEOhkQ64
|
||||
wDcAT2tT4asJwLMNRWrJL7e5eou3yKv0y55IRzvRpZf3SAe51KZKcdL2Kk11pR0alFob05yCSyBp
|
||||
/Ct+mSLU/97jXFNEgtVyGfjQxvMqX6SqtAfSRmggVY0O8QVbpEOR5jZJloLZo3PHpTb1CrtivTN+
|
||||
5RAvrH6pGy/vJRRG4C3/NgkNAbuMLZH2K+/2yupXVDdUPXmFQJmFC4AkVEVtU3ji6v1NW6Mtv6x1
|
||||
hPsM0JctMrtCqC0CQSvlZBPnlTqIBN9XAbNx3oYg52WpRbLeX20jBrtCEloHi8kve6JUizCiNumv
|
||||
0slY8srLHuhHqG1UEeB3eG755ZkLF//3qUO3HyVINklUReVgHR2WCd//aT9OLiY02hZ9tU6S8IPC
|
||||
daapHXoH0zrwlxaZtknwDaD2E22SobUr7ehfIBtVyJifhGxSIx1Ke6gv/KDHkibWQYx2I/kqnfQn
|
||||
lXwHV5jeEelg5NUGjSrt1a4fthdJSaSj3aCKFKITea5FfZVWqxmmGoX7k++RYX7Z3qK1Gyppa2/K
|
||||
L2u+7Ue1jb7sC2CXX+ottCO/3NZCT1ooKiAU3kwFrIXPvtCmdtLWHpFe9rzsI1G15VWaRhVm3QDG
|
||||
Ra7QFn7U0Wh5eS+IXn6JiyctspRIetPL81pHu6FIV4jx8h5fy8t7/vTyK5qQdDAauUUN3X0E61Ff
|
||||
bSjwQpNB0UGtreL/ekTIgXPM5BVmuQp4itsTQhDMs/RKQmJaG+mgshqDaAbRx9XHagARYZqPEiPJ
|
||||
KH2VVkU4o6AgVL/bCdCb88tNySs2Vra/7OO3WJgZinHihJLzGMV91KIuk//KjkshGKcT81TS8W9g
|
||||
nBe5qjuIGtOoIqFx07iElgDmSjooSahxNfKyB74I3kKJYuU4thIB+vCgY5JjHoup4XiojXxRNcJk
|
||||
ta3dl4C2GO1tlpOGIaCQRV3ROtq5fUVJBzgLhJt4IP1EEPbnn3/WaCwuvg/twz6Fh7b5c8+vSEJt
|
||||
01SDLy3EWUceN8mrdS2/HH2VpuE6kx3JKy/vBTLRcxBWXt6LKuqrH172hX+rVazJoLH8cttRSNCg
|
||||
Q9lGX3ULktoO8TDSTCSi6jTaQS0M8eCin7fRGLW0Ftim/IqPA0j5oCT0K7UJYkhbKA76Jcf3FlVm
|
||||
V5SXPW6Qb9yrrAG6ZIv6qhtRzJ+LnlZf/QBtuA64kyulBRQS6cDXCCJw+kbhbUqv7NaLkHJI5Vjr
|
||||
Tjz9SKkrDLgjLuBGG5dIhAIMWvII07R0ohh4N7W84mUfRhTOmgI7ls4k3Mys+NEihWlVEkFpXvYg
|
||||
XOpW6ojZsR1yMFuQXMl02J7lVx7pCcdbK1eokxyoLenJUAs3C6uFxWOF9cKzQq7worBa7AzLSgtj
|
||||
hc1iT7GzsFB4XrxeeFpMF1ZJYQnnK6aKaX6lkCv2FgdIIUcKS8WBwkphtfCssFp4XnheHCg8JXBT
|
||||
YR2Gk8ISKaYKS4VNGLtYeFbYLKwXnhc2i+niQBV1LbwobBZWYMnFb4t9hQX+6GKqOEgK67CChWKm
|
||||
sFrYKHbBhJuFRZgeF/Yc5rMurBY74Y/FzmJXaJpbuM/lRArPCwthyG4D+fow3sKDqp3Bgjf9qW9h
|
||||
BIT+ovFtge/YHissFFaKnSA8UtgA0cDNxXRhIUQ7teZ2S+zLhU1SvFZYL6yBDgovCs+K18EUwtPk
|
||||
wkhhQRjQZuFpYR3s4QX+kiusoYGsCI0uvAtlFkJeKKbBYNYKC8XrhaXCerELjRREuVRMFzaLnSDG
|
||||
Wtnm4K5iqnjdl1wXfiymC+vFzuIAf8iwS7Sb/lS7cBP3tlBYKg6iR9XIWVDvQg4MirPvatG7DNsR
|
||||
+nFSyIKjFJ4VByw6Dv52DRZU2CCFRRhd7AKlwYMIug048CIKagCf8S16abEHJCLcNYerRWd7R9Ze
|
||||
uFkYKowWxgp3C/OH5e6F57DUHMp2FRa3UthEy+kqvCgsABqIHT4FIYEPLqAuV4+TwgO0KA4AXNK4
|
||||
3zWUcwadKFe8DlM+K17Ha8VUMVPIwXXrGp9xqZBDIXWBwk8UUwKRFohQEkAYWAOsg4PkImIbGFpX
|
||||
8VswMQDK1cKz4mBweaDwnRsaYa7VwhriEToHh9zrvgWD6ilgHdzM12HNz0GSIeoFhY1CrvC82IU7
|
||||
LfahFdiP9q8coOxzgPFO9YBPVz3R+6siFG4WB0ALxU60dJAexIHCE3i2V1GhkCsso8g60Qo20SfS
|
||||
xcGAEgPESbSz9El3DMxxm3PFPwgp1YGzsHqUSwS8caDmbgAW7vOgPzDCFbCttcIC+AOYXCEXVIhw
|
||||
y8EKUQveFQkYjva/JuAT3AujaVW47/KvUrgTB7ER3OKz4iB4BcyWKg4UngEmhyhgQAjEqLCGIewJ
|
||||
h22OX85KC2sgHkwSIIdAXKgT94KTeOTQHmpBBSGlHgq+DVH7KIyhlUG+8gwetITBbUXYQWHJTnH8
|
||||
cDlEMQQzgmIXRDjYCk+LwCiWcUdrlunUZFiAiIVNEWcXi10WVL3gD/YKg8H1ksJIYRNRAibFWO4s
|
||||
Bt2tOOBXPWmQEuUKqyEqKY1yKVDfh6+pFMZqjAzSpU0Qgfhbn50Qb1SHMDABzHiXHL8TioE5cEBh
|
||||
s3i1OFgVRkDIgM1o3MUuJ/VwW5mITXYo4lHSNUe4mk1tOOJxiFu6KyB4VXEKNwrrxXQhV1jCe67X
|
||||
eWSIwk6DUG6b7QpSABtthPkUNoJKPoU7YKQASkI26/7lH8j4IPw7Y32KQA0AECC8LkusLwxBaoxG
|
||||
sBJYGCrcQF0Apq8RkadtFvtQAOtcrr4VIvf9gTWiqh1V+6h/pajwPQ7HZLXYi1kVhkQQ/dJRzGYL
|
||||
G8gw09xRiik0V0x10SOK34LQOMFEbT0RC3jqV0xC/VphcqHeSBaOBxWY6raMcfiD1poA4oophIA0
|
||||
pq2eluRZgaqL6EA0il2FZcxMG8X1hoUpz6DDnTLDrdy/SFWYgNUeg38Q2ooZmIrn+MiI1jkNFCTt
|
||||
WbHLjge58PTTq46F4cIifsInX2BAHMDsvSuoqFUX330yPYG9Fqo/sVBrqZhqIPUQ5a+G8uexfMFx
|
||||
4+q01St5C6qSNXpWlf/kbGZteZgzacP6mQDwRawMbfAAiTwWkoxvG3K1BiWPHEeBGr4ctrqGmfBh
|
||||
MvN30OI3R490NHuW4qzIvPv8u/2x1e2r03ub323fmwwqw20v9Io7bg7uTqe2+9Z37m7uZtd373fv
|
||||
3O7bGZ7ZuTa09+3admZhJz25e7/bzgB2Z27tzS3sDM+8yc1ilrH34sedb8f5wL2+6e21RffwneFr
|
||||
2/1j2/cmy53poDKZWHxmYefOk+3MM78CWYPdepS6tteW+Ar9K2I7w9POMP8KGF/d7vPvtkc6gwte
|
||||
NeLbnvvxzeYACXWAYDvz5M3z4f2H3Xtzm+9Sstpev7V7a3L76ZP9vms7txec/XlWoHaufr+90emI
|
||||
37vgtLt0d29lpV7NsFcsNv1zI7OTuQ2lpn9uXP38wmflzvTObHZn9freRGb73uSb3CwM5ecHyHa2
|
||||
e/dG7+69Z9vPh4ShHL4ktHs1szM8c9hi0M6d0Z3lob25F/t35nZms/udV/fHnm5fG4P1PurceTC+
|
||||
f2tu94e7J3Z/urebfrpzdWp7cWhnpGv/3g1yjOxkbnORvdmc2B95uDPyQqzetxYDjxzt23941/Yl
|
||||
37rL3ou7O0tjO8NXq4b71Vn27/bszT/bXX9M/kC2M737Nx8EV1e2sxO764/LnWm7trIz3Lm98B2f
|
||||
a2dmbGdo4T2WVkDWqwPbmYX9zqs7A1NexZTtF893h8YDSic712/sZtf/uXHfDWz7Y6v7Iw/f5Ppt
|
||||
hHvzbBxQcGlzd2xu9343fzCHlDe59Z3lyf2+fqFOn1oIv8FZdeMSCB8F9jW26l/mePOs582Lke3Z
|
||||
u/vfbwp3Dahk7Mxmt7Pd3I75cxCX+cP2Rx7u3322M/uQW+7u5MD2+nVu7RYi+9Yf9ubXtnsy28OT
|
||||
tc6Lvmr5v3sqzxKDWGH/6N7z57YplzvT2wsje/NDboyqcaKGBQJ7hu21JT6nXz1gZ/bR9vAknz1E
|
||||
FcCOaf8G3r99/c7+vRtcdajI/c77ey/6nAiyNr7ds7Yzen0nc9uGnb0XP+yNDXLtC+kFcvDdH8Ds
|
||||
t4cXqnDFi3LvrDzb7p/kywrBrrkNb1+9tvNgfHd4IIg4czz0J8s7393d6e8OIMniue4cop4Pb9/4
|
||||
dr+zM5ALb2d6d368sTMyvrN+I5D4cgvjt/zS+TiQ+/KFhmK7u7ce7GRu7Iz27c5uljtTe/Ozb55e
|
||||
3fnu7t7j1PbNwe3Vwe0et8s04K0czLbXlvhTg0gqH7XXt/xBeen28KRt5G9ys47heNJQkf9df/wm
|
||||
1ymAtSHPFMgzN7iTueHPJhtma9t967vfLuzfmdt/eFfIuQEV3O+7tjc/FMT39h/f3u7J8HDBw9A/
|
||||
NwZ3MrdFgBhaeZOb5k7mYKoPgxM744ZqAb5zpxcZ47dxNfMtcwxw7mzIuHaff7f7bJgLgscYzJD4
|
||||
DAIzbw7a0uNZXxCF8g/SvtL55uiRK9STGZW7Hpa7NspdY+Wu5XJXppx+XE5PlLtul9Pz5a7pcldX
|
||||
ueu7ctdGEFOCLLPcdR+mSufKqcly1/VyOlvumsJ/M+XUUDk9WE49KHf1ltOz5fRTfNZNWwrlrm9x
|
||||
GRl4dFcXPD31E4zselTuulPuSsEPqTvl1GY5da/cmd4dmNnrmy6n5pwZ0qs4ww34N/XTzrfju0/u
|
||||
u28JIla1koDn2zK448ez/ITmxaRweTu5TDn1IoBziY1wAT4up7pxRz+U098Hs7C6Pc2FOs7tyDQL
|
||||
N6afltMvxA+puXC0DLU2W+7qBJmk18pdE7iEOyCf9DMUUabc9eywbG0/NbV7axLV+ni795olnHvl
|
||||
9IAvZ6sz85v7twfKqev+/K2xbc1ZLwqgIh6UU/POCfBy+ube0kY53f/m2d1y6jvb+Mqp78upOTTu
|
||||
KTSXJzCPIHbl1CA8QQib/3tzu+/x7o3eWgMOSfhqDBGIn1h9OsdVcVgGWE7Niz2AKdxG73iIun2G
|
||||
ZjFRTl2zDRM2DL67Ue4axn+vnrA2mNkZGNveWN2ZerDX9bycmtuZnNq/d6Ocvrn7cH1v+hpXZTk1
|
||||
UO5MWbdcJbiLa+Wunyy9XasSii+P3O0e2x8bROELiPHlkTjxqANHsO10Od1f5XR+tHJ3pZsTynJq
|
||||
Ym96tpxO8bUGk8vdlW6LVW5neuHe1Ivt67fL6f73SCn3U7md/h/LqcGd7PB237qFM5toeA+8GOZu
|
||||
99je49uO3/nwzHLXTe5zJ50A8SbXjwqpigU1IWNvbHI3u849vdyZ2pl9tPtwvZya5gvene/e/mEJ
|
||||
7xoD304JTHBDdq3v+PDT6tvm+DO8iarlzVa8S99s+Fx/DovjlzHSzZVTL8rpm07eGUBm8fnfo9PN
|
||||
Vz+5yg0x+6hda2q+2gNz6NMuVwaZ3kbs5mH4jivq3AtFh3Ho/UaQN+GEawCQaswTT7oO/pcagkeG
|
||||
IcwNMgfYAUefq7XABL+CYQOjdsIHf/CLxo/3JdiN8pZB19PrvMmTe+NS3uRmQxBvpNukLsnBR3xQ
|
||||
Ep55Atjt5HbCxtDunMyuG4XxkxU+RcxsjO5vng0EO3Igad+e+2G7J1OvGz/qLnwAMOkRBrhpez+h
|
||||
mTwHxO2r18qpn6x4VmvUQSQf4ivPmbt+CqD6/cO7SwMBVL8+q3VQpp7z72SeCUT35fzl9HOUDIAF
|
||||
p1VvcrOB5N8eGZL2OyP9mT9KbAFiRPf17e+WIal4nN57nIIUItVfTmfKqeXtBwPldGpvc4MjLS8W
|
||||
1NqVR13ACjGDlpLrnNqvTOBUB4gt/Nop3n/ZAM3AtoGJvccDQs/eZYPMuG+Ms0J/w1rC9tzgm/Ve
|
||||
L/D1ry74kY+foPadusWfvn//B/jL0HXM7wZ3763v3nrgBkPvMgTuI8NzjaBiBFT4MyMQr6rzFStz
|
||||
ciWslrC2eybfPP/eKy0IVbOoTzG4+/gEtIByhjBjK80InqhhdQORmpO3eVwapC9cB7zYIULSpLvA
|
||||
QVz5vUeQ9yt++OeN2xtre9MvrHTx3bTxzdEjLapnjeRgevJg6u7b3lFS+eEpeZtaOXg88/ZaYE0E
|
||||
B397tzKyUpnorNzrJL8uDFXGFsjb74YrA8MHN4bJ255rlf7s2/7cwfiGLa+Dnhz85UmqcucGqayP
|
||||
VZbuvh26W7kzQyo/3qgsOe3ESvfwwVI3jrqz8OvSwtuhqYOBzMFANvCoRSUz/OsC3ombee5X3Kjb
|
||||
skedgq/h4MakfznDGna3MtFJKkP9lZHB4DKGo4BQ5Ytf1zpBYiN3HVGN5EDQ3SuV/my4+sXbW90H
|
||||
Y8MH14YqPXcJ/PJ4ptIz8C4N5spoqnJ/6u3Q3V+XR8nBQLYyMGwr7G6lO1e5f8u3dOGyvjsrv+YW
|
||||
wpUshHGM3OXFit8f3L1x0H/LLlRURrrJwZN7lTszB92PKv2rwnBIZWSFHOQGD0Y2KvcXKqOdMEHl
|
||||
xejbdBbuPhgb/nUtB8Ic6SaVpbtgFFVGd/h+NCxytPuQ1QhYC6z0bdcsmv/od7+ubbwdGiWVzF1S
|
||||
udPr2hCsfHnl4PvhE5X0TGVk5W1/jry9N1T5cZz8+mTw7dAwbAX+MJIDFS0uHKTvkkpu7GBqhbwd
|
||||
nDzovoF7vfPctUvfekNldPhgegpWgRJDZ/atOFSGuisTKTHy14XO6g0ElRv++w8HT7pJZeS5sAz/
|
||||
EsPBYOfBj6v2yQBxK/jg+6swVLpH33aNkLdDswfTU6C1XxduVCY6AQLuZKo32qjUcDCzUhkYfjt0
|
||||
92AgG1BqeDuYOulC3JG71QBbBb8WpI4Ovb039HZomNQss5scTN0C1xsbBtw9eNzjWqdPJYEDqpjN
|
||||
u4KA1mut1IXC3uWCg4nU29TMwcQgAbf8MUzPu3Jn5tfcQqV7WDygsYdULQVsVrgHR8Bf13IHP06S
|
||||
ytAYQFV/FgRZWZg8GJisCTe+FYGDyY3KaGc9uty5AasTXg2ubCOMjY0hXst3IiaobWz44Pvhxlut
|
||||
Qd8VcrC84nqKL9N3PWRFPKSRCXvxe/5MUsl+F5bgO4EdguqH5PWV0eG3QzxBsU3UTekPrnf/ujpD
|
||||
DqYnKyODRESWOsx827sKIrZMz20sgQT+YLCz0pVyUiE/4v723nCle9FZagi2zj0I8D63QCqjmAn9
|
||||
utT5tne0KiT70XQO8/4E/WBhuLI2GEDQ7fDOI5nl2vXc/OBmtjL9vHLnRiA9r/QOQp5z8KS70j0C
|
||||
JhdIzZ2hgdy8MpqpDD0PRczfdoEzHyWVkbsH8zlSuX+rMvScHCxcJ5Ufuw+WRg9uIdBWukYq3YvV
|
||||
FuJBxTkmY5BESTXywHBk3JF2Y+/6rQm4g3GAeAfLVibnyb8PBrIH4zmXB0IUGrdua8i6K/e6Dx4N
|
||||
NoS+cJTbnSa7ecVdTGWuLZCDmVxldBjkD7bdNfL2dqY2Vanj2gJ0BdyKKO5HuEWwqIrdPJ66MrTR
|
||||
bqBCTtyCMbVh2p9hV8e98Q3hL57i86HWIlWANQhk8Z6jIas+6IaNVn4YfJuaEUImx4T40WFcZLoq
|
||||
YXVFLz8GHT4pCi9fQCHNkzOXekqZUk+p15wuZUgpU+o3J0mpq9QNP5iLQdTZnCh1m9OltLlGzNVS
|
||||
ypw1J8xpAn+1psrgL73mKv9rT6mn1G8ukVKq1E/M+VIKbjAXSamn1G1OmLNmDmewxIjzz5kT5qo5
|
||||
RczJUhc5xhc5ba6Wuok5iRfnSz0N7sDxgRS71AOrN58Sc7zUXbpK3PLwo9t436prqz6Uu9TD1+vP
|
||||
uM31Uj9Kwhnsz7bNp6VelG8v1m5QulVr92ffDW43fyr1l7q9ZBlMxs1x86k5DxqBbSybk6U0zNtt
|
||||
jsOizAlz5l2IuTltrpmTpX6YLIOPmDDnwR4m0GamzVX4yZebmxOllDltztap15ug4+yrpTSpkVO1
|
||||
UDqtdwzMR6UMZ+3isp9REnPanIZ9gNmj7XNHIPjXxVJ/KWXtbtGcNifNCXAw7j8Zc6aUwf2jD5X6
|
||||
zAVzQlxEy0nhNsGip81lcwY1ipPNm3OlTKn7HQsAOMW4uYr6XQxbCIA9kFK/uQxKLPWbT6tkUsqY
|
||||
q7Aky/nMpzC+1GMpd9qctHCkz1wo9aHgZktpsISeUi8xH5kLpUypF2WHEDLLLS5jzoo5Sj1ctKV+
|
||||
c/aEJcJS2nyKmTJqzTYOvOy+2ZwBtYPtmVPBFQWEMwAzcxK37RiOb12Bj50GxZrzPphozprTICNY
|
||||
10M+Z8DnCcz5UhrcBmwlbS6aE+aYORVcfEBAX8QAMOF8nKDxZO+vHGFOl3q4RrrMFfCHp6BwbhWz
|
||||
5nyp35z3KkVgfADV9XFY8ClFQGQC0zCnTpJSGp44CQ8u9WOUqo1dwvfQTi1V8LA3BVZnBQRnhcLx
|
||||
uOQgrjoqnQqqUJgT5hza7QSYFF8AhirvagWugLtK1eL8KxbmbCll5vgNoNdJcw2wJW2uhahdCAcv
|
||||
9ZS6hH7EIsAr0ccarAfd2MxZ+6pFAY40T9AvJ8zpEHULiJzmDNzbGPOsqS3w8YFOgAREoYEQFQ3E
|
||||
6wXYB+BSD3+u48FCay4vPg5uPCGW5BHJgmsc3DdCPtKv2gFeYi6WrtWsIETpw0pTeBD88AcLHL/r
|
||||
Ae9CMR6zXdgyQ/MpZLHEXEE8W4OAMQ+Q7E4xRQgIQP7AUoiD/tykZ0p93PG86iHODnrNFXONDw4o
|
||||
iAhbBS+dMZ9yKJ60bB3dOKgeUh2UvGsiCDYQs1OWDfnURiwEaZxC15dIzDnIeALrI2jh3bYzl3ow
|
||||
+1gMrJJUW7U9SWDJBBYMyGAuhiqbYFa7AoGQP8hKXjAZydiOOGdFhXm83g3IkxEre4IO3GujnEc5
|
||||
hfu7A9PuMBJUSrHjqLgfoe+DHoWAlNZ8IjC3xjY8yyrcfe148cicMxdLVz1LKjVhaBxCLUoq4KuQ
|
||||
EKjNn4g5Z66ZjyBtnoBn/YQqGWsYnWpZklc5xXwEXMgyXAH6XF1+RRVc0Lwr87DDkTnIsUpgnJNt
|
||||
2EKaNB/xFKPUZ06EqKzUCM3xtgYhO6i0UjOXsDXHTqdL/uffSz3mCtQdLF6Dz14BtoTAXpP325Gq
|
||||
1AXO5M/Qwh5//5fyv8NrB03Zsy7zDv/7dVcFppTB2gsWE0q9oM+JnwdBFz8PApNYMedBvYtg7fPA
|
||||
NEB807DszOspMdoJkHN8z08wMXw9Ver9edCc5/RpEqZZNhdLGXOx5gbg+/BoK08o9fw8SMwVuBRY
|
||||
lHk9Ver+ebC0yR8A0GXmuCf7/D/43GIKKMSgBPyrMc66YbvLPAQG1GMegjDNZXvBBOQYXIjB6ksv
|
||||
ImNvyTkaaa4AHAUXXUqbPw+aK8Age83cz4OYGQJmoJbepdwCm5jDLGnZnHYUDmkSJE+TGFz7CAJj
|
||||
N6oUrvvWX+w9ocnBDEv2xP5lGNf/auHnQXOCl1g8TE5MPwNifz0Pyb25+POg/aDj8FPm9dTPg790
|
||||
DgojR79AK8UJX6M3ZPCXecu8uf5BrjA9zAZ5njDpXzoH4S+lXmHWhyys/KOVj/zmMK9VoLXA4qc5
|
||||
6tlL5ZuBlPc1Ovg81l0WYQsEzWTe2sD8cdgzoNmSLYlSylwSNsihpNRHuCea8yfMZQs8hAoXxby2
|
||||
fRwjqKDSJiwJDEOk0DB3A7WHr6e8xhVNgvHB7kJWVfpw2FIttrm82bd6Qnj55OfBUhpsvcdcEzle
|
||||
X5Xhhqmk/DwIW68qpIhpuZuBOkq977OUMoEmCyFxEvy1F1174udBRGSfOgoMnIetwq7nzUXQB5e7
|
||||
b0nl9ZQ5Xeo7aUUfJ87YZoX2M4Nu5cgTgvwE+XnwNVIw14qFwISRO8Ee7A2yJXPCXEEVo6zR8p04
|
||||
41thcT3Fijne1RXLvexNCDjgoaoKzjxrLbyuQvCeXtgoymbBnPt5EEHVPUtA5YVLFNcErlYLCu6V
|
||||
mYvon3UbcOMDSA8UXuoBe6/GBLBPy42xsD+P+IjPE3ASokbTzdlkaYBgkWka0QhdwwOFJ3DdrniY
|
||||
sYHDvyzDff6XzkHu9QLuOQItvgaLhriA7G3+OESTp24g84tywbWZ+lyq9rmYfvVyHXsWZlC5wBq6
|
||||
yaHqMugj/OBJdbbz4YszHDdnhbGA+UFKvQYygES+l2uYG8MS/mip2jOQ1Hnb4WozGElqUIM38kzf
|
||||
MyfOflLYtekOUaqxKzOwacfBG0TO36pk83reXOL4F1SucS1n3hG7R60GswkQVZh6zQwsAd1G8Emg
|
||||
HUjazEleufav3LjN3mO24DKOexJ7q+FKOk7mjPvuhSYmOjHwuzlks6vcrp8CWEIINQdxfTM8qMKK
|
||||
ic2/Peo5tYHLpY8VKyH2qejA/lzHVWpvf/8VHSDr0F4x5xuYkGdJh0d2J/hgkr7oE0YbVnrM6ddT
|
||||
EHiQmlni8i7wQB74eh7CGuFuB9greKUX5wL1rvAVEYtdPSz1CLRpVOtxRwysAU6j/fVWbce37ONE
|
||||
xKqsqSZDwbnMQQ6QriRddJIbizJMJagmd2iY6ni6N08wgipEtUbPRe0yXhGPg6pEsD8w9MnXU6U+
|
||||
HlZEeIRluf5vItWssB53gwtCh8xfUSt8edWCg9ErkFf0ied+c/RIs+RZ+9nKzm2ND+C/fVvZ9Fb2
|
||||
wdb4ANnKruLvM2QrO7Q1fm0ru7SVfbGVnd7KLgSVhrayPThwcmu8j3hNP4l/fbCVndkaT21lh4g9
|
||||
pOrZ0/jTMNnK3trKLpCt7NOt7OZWtg/Xw394gEuExV0nW9k7/D8P8D6+8hz+y4fhs4e2xjM49bWt
|
||||
7H0+7dBWdkE8/hjZys5vZZ+SrWwv7mOI7yPEpnI4yzXYAM6a44OHcYtjW9mNrWx2K7uJ2xjPbI1n
|
||||
g2pTXg+atlbt0kzVLvzKV0G69ShSVe0D9/UUf7/uX9uqv20S7+zkqkZB8PX6F7y8RDG/NZ5GTS1s
|
||||
Za9vZZeCS2COXlEVjaflvwjTsa7lPJ7nXzOzTG0BHzRmCf3JVnaSuK51cnsBS32HQhouiev1MZ/Y
|
||||
ZdK290yjKia3sl18wKTLTR7UGZBnhc1LaLfxr0vODN6ltnfyJ9SYKNChk246H1lBV35hKxaVk9oa
|
||||
z3JdguZmhKTh4lUwS/DHhSrgGeJghMqA63wNHGcmj/H/ODiSw7XO4oAlcO3xTseihXf7lOgOj7zw
|
||||
apT1WG6aqLDso7BvSPlYe46jZoY/OcshucpVpy2PT29l7/PtD3FAO04soc3g9FxpYydc0gPruu4g
|
||||
suNTwg160BRSsDSY8gVXL3/wMP8JNFwF0Ztu9Yxx887gopdqjVFow7cECGuBPfDVr3EJLGDIebGV
|
||||
zTYIWr5FQdzTLLcJIc4hvD/TOPw5sg7xthXK6QXe9ICr5iHH1wa31yfHaFN30HTvgItY1UKPWbOb
|
||||
77FeiH47b0PWJn+uCJP3eZz3qhkKs8k+xo3f5vdwl5gUt/nUDhHLU5ZNgtGdtFOJHNcbN9ROriD3
|
||||
Ot05DLFThAWMDdO1kFIfoy3YE/ct2enL9NZ4F9ge3DPqBHrfdMYzJVqosn2f2mT9KsVeveuTNmbU
|
||||
rtjJsXxSE++aJZcJIv6SLZk0V0AOPaQPMwprpoC6JU/lbCSpXVUVvg3ZsCMwpwrj3Hmhk5X6QZrr
|
||||
2rgQlhDOjFjG+FULF8AmGqSGvjVOnK4LbWTedpynHPIOFfUaxwTnuhMCiSsE1piXf5G0IeIt8YSo
|
||||
QUo4YIke/3Y8bObiuJUQh9g6DyW5qmUHF1cD1lzNOfzqqw0XLHbJ44ul9+PHjx8J9Z0fm1s0FoVj
|
||||
x3f4Tx+0IuuSPA/lM2hXE3YcrcaMY54m+ADxdAkn2ORWGxj2h6yEEgZM4CPmt7Jzbol5IZOw5MAK
|
||||
L0o1jZtb5Q/wyA28SryWcXMZveATVAklRM23Cg8tyxd7rAKCHAq203rimitM2jDqVxKuz4v8S8MY
|
||||
lQEGAyrDoYCnDvDri8Yous6t7A9b432BZWMrfnIylnXAYhUV8cRC5j7c6aPAIrLXjULCtR6e3sqm
|
||||
hJN715SrNeuKRg3Bwr/IbCX1d45yeb6okjf8OmS704KVZz9xGON1HH8NzboP2SuPx3cwq4WZ523f
|
||||
dDIvV7rdoCLtzpxqd2rhVfgPKHkYTAD6/dZV6mBjtpJU25I9a9c4Nm3R376qhCNUetWwlo2zpLmW
|
||||
/UIuF753kbumLHbI8olgQjaXTGGu6VQBqoj4D1yFvHI2Y9WQOu3Sk5UnNaiS2xnDyaBEIKhaXoOm
|
||||
jVPx+yI9wRmPEldG5hJ5lRb9Y5B/Bd0n+fbBsvD57mZQeb2mhObtzlUPbPyMhrV3S5YPeO2H1zqs
|
||||
WCbkPm9l5VYlyYpr7oRI5OK37Jw3RAmxQclBqMWvgP9+yFsDW7IQ4XC0C3fwzTf/F3sSfF7y7gAA
|
||||
`
|
||||
|
||||
function loadStrings () {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"version": "0.3.1",
|
||||
"version": "0.4.0",
|
||||
"strings": {
|
||||
"en": {
|
||||
"extName": {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"manifest_version": 3,
|
||||
"name": "__MSG_extName__",
|
||||
"version": "0.3.1",
|
||||
"version": "0.4.0",
|
||||
"description": "__MSG_extDescription__",
|
||||
"default_locale": "en",
|
||||
"permissions": ["activeTab", "scripting", "storage", "sidePanel", "tabs"],
|
||||
@@ -12,12 +12,25 @@
|
||||
"http://*/*",
|
||||
"https://*/*"
|
||||
],
|
||||
"optional_host_permissions": [
|
||||
"https://*/*"
|
||||
],
|
||||
"background": {
|
||||
"service_worker": "background.js"
|
||||
},
|
||||
"side_panel": {
|
||||
"default_path": "sidepanel.html"
|
||||
},
|
||||
"sidebar_action": {
|
||||
"default_title": "__MSG_extActionTitle__",
|
||||
"default_panel": "sidepanel.html"
|
||||
},
|
||||
"browser_specific_settings": {
|
||||
"gecko": {
|
||||
"id": "memento-clipper@memento-note.com",
|
||||
"strict_min_version": "115.0"
|
||||
}
|
||||
},
|
||||
"content_scripts": [
|
||||
{
|
||||
"matches": ["http://*/*", "https://*/*"],
|
||||
|
||||
Binary file not shown.
BIN
memento-note/extension/memento-web-clipper-firefox.xpi
Normal file
BIN
memento-note/extension/memento-web-clipper-firefox.xpi
Normal file
Binary file not shown.
@@ -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
|
||||
|
||||
258
memento-note/extension/scripts/build-firefox.mjs
Normal file
258
memento-note/extension/scripts/build-firefox.mjs
Normal file
@@ -0,0 +1,258 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* Build script for Firefox Add-ons (AMO) production package
|
||||
* Usage: node scripts/build-firefox.mjs
|
||||
*
|
||||
* This script:
|
||||
* 1. Sets ALLOW_INSTANCE_CONFIG = false in sidepanel.js
|
||||
* 2. Restricts host_permissions to memento-note.com only
|
||||
* 3. Removes side_panel (Chrome-only) from manifest.json
|
||||
* 4. Keeps sidebar_action + browser_specific_settings (Firefox-specific)
|
||||
* 5. Copies and generates icons from public/icons/
|
||||
* 6. Creates a Firefox-ready .xpi package
|
||||
*/
|
||||
|
||||
import fs from 'fs'
|
||||
import path from 'path'
|
||||
import { fileURLToPath } from 'url'
|
||||
import AdmZip from 'adm-zip'
|
||||
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url))
|
||||
const extRoot = path.resolve(__dirname, '..')
|
||||
const projectRoot = path.resolve(extRoot, '..')
|
||||
const publicIconsDir = path.join(projectRoot, 'public', 'icons')
|
||||
const distDir = path.join(extRoot, 'dist-firefox')
|
||||
|
||||
const colors = {
|
||||
reset: '\x1b[0m',
|
||||
green: '\x1b[32m',
|
||||
yellow: '\x1b[33m',
|
||||
blue: '\x1b[34m',
|
||||
red: '\x1b[31m'
|
||||
}
|
||||
|
||||
function log(message, color = 'reset') {
|
||||
console.log(`${colors[color]}${message}${colors.reset}`)
|
||||
}
|
||||
|
||||
function ensureDir(dirPath) {
|
||||
if (!fs.existsSync(dirPath)) {
|
||||
fs.mkdirSync(dirPath, { recursive: true })
|
||||
}
|
||||
}
|
||||
|
||||
// Copy all files from source to destination, excluding specified patterns.
|
||||
// `exclude` accepts RegExp or a function (relPath, isDirectory) -> boolean.
|
||||
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)
|
||||
|
||||
if (isExcluded(relPath, entry.isDirectory())) {
|
||||
continue
|
||||
}
|
||||
|
||||
const destPath = path.join(dest, entry.name)
|
||||
|
||||
if (entry.isDirectory()) {
|
||||
copyFiles(srcPath, destPath, exclude)
|
||||
} else {
|
||||
fs.copyFileSync(srcPath, destPath)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Read and modify sidepanel.js for production (lock URL to memento-note.com)
|
||||
function processSidepanelJs(content) {
|
||||
return content.replace(
|
||||
/const ALLOW_INSTANCE_CONFIG = true/,
|
||||
'const ALLOW_INSTANCE_CONFIG = false'
|
||||
)
|
||||
}
|
||||
|
||||
// Read and modify manifest.json for Firefox production
|
||||
function processManifestJson(content) {
|
||||
const manifest = JSON.parse(content)
|
||||
|
||||
// Firefox doesn't support chrome.sidePanel — use sidebarAction (already declared).
|
||||
delete manifest.side_panel
|
||||
|
||||
// Firefox MV3 service workers are supported, but background events are also
|
||||
// reliable with background.scripts. Keep service_worker for simplicity.
|
||||
|
||||
// Restrict host_permissions to production. Keep optional_host_permissions as-is.
|
||||
manifest.host_permissions = [
|
||||
'https://memento-note.com/*',
|
||||
'https://www.memento-note.com/*',
|
||||
]
|
||||
|
||||
if (manifest.content_scripts?.[0]) {
|
||||
manifest.content_scripts[0].matches = ['https://memento-note.com/*', 'https://www.memento-note.com/*']
|
||||
}
|
||||
|
||||
return JSON.stringify(manifest, null, 2)
|
||||
}
|
||||
|
||||
// Generate PNG icons from SVG using sharp
|
||||
async function generateIcons() {
|
||||
log('📦 Generating PNG icons from SVG...', 'blue')
|
||||
|
||||
const sharp = (await import('sharp')).default
|
||||
const sizes = [16, 48, 128]
|
||||
|
||||
const icon512Svg = path.join(publicIconsDir, 'icon-512.svg')
|
||||
const icon192Svg = path.join(publicIconsDir, 'icon-192.svg')
|
||||
|
||||
if (!fs.existsSync(icon512Svg) || !fs.existsSync(icon192Svg)) {
|
||||
log('⚠️ Source SVG icons not found. Copying SVG files only.', 'yellow')
|
||||
fs.copyFileSync(icon512Svg, path.join(distDir, 'icon-512.svg'))
|
||||
fs.copyFileSync(icon192Svg, path.join(distDir, 'icon-192.svg'))
|
||||
return
|
||||
}
|
||||
|
||||
for (const size of sizes) {
|
||||
const sourceSvg = size >= 128 ? icon512Svg : icon192Svg
|
||||
const outputPath = path.join(distDir, `icon-${size}.png`)
|
||||
|
||||
await sharp(sourceSvg)
|
||||
.resize(size, size)
|
||||
.png()
|
||||
.toFile(outputPath)
|
||||
|
||||
log(` ✓ Generated icon-${size}.png`, 'green')
|
||||
}
|
||||
|
||||
fs.copyFileSync(icon512Svg, path.join(distDir, 'icon-512.svg'))
|
||||
fs.copyFileSync(icon192Svg, path.join(distDir, 'icon-192.svg'))
|
||||
}
|
||||
|
||||
// Create XPI (ZIP) package using AdmZip
|
||||
async function createXpiPackage() {
|
||||
log('📦 Creating XPI package...', 'blue')
|
||||
|
||||
const xpiPath = path.join(extRoot, 'memento-web-clipper-firefox.xpi')
|
||||
|
||||
try {
|
||||
const zip = new AdmZip()
|
||||
|
||||
const addFiles = (dir, base = '') => {
|
||||
const entries = fs.readdirSync(dir, { withFileTypes: true })
|
||||
for (const entry of entries) {
|
||||
const fullPath = path.join(dir, entry.name)
|
||||
const relativePath = path.join(base, entry.name)
|
||||
|
||||
if (entry.isDirectory()) {
|
||||
addFiles(fullPath, relativePath)
|
||||
} else {
|
||||
zip.addLocalFile(fullPath, base)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
addFiles(distDir)
|
||||
zip.writeZip(xpiPath)
|
||||
|
||||
const stats = fs.statSync(xpiPath)
|
||||
log(`✓ XPI package created: ${xpiPath}`, 'green')
|
||||
log(` Size: ${(stats.size / 1024).toFixed(2)} KB`, 'green')
|
||||
} catch (error) {
|
||||
throw new Error(`Failed to create XPI: ${error.message}`)
|
||||
}
|
||||
}
|
||||
|
||||
async function build() {
|
||||
log('🦊 Starting Firefox (AMO) build...', 'blue')
|
||||
log('')
|
||||
|
||||
try {
|
||||
log('🧹 Cleaning dist directory...', 'blue')
|
||||
if (fs.existsSync(distDir)) {
|
||||
fs.rmSync(distDir, { recursive: true, force: true })
|
||||
}
|
||||
ensureDir(distDir)
|
||||
|
||||
log('📋 Copying extension files...', 'blue')
|
||||
copyFiles(extRoot, distDir, [
|
||||
(rel, isDir) => isDir && /^dist-/i.test(rel),
|
||||
(rel, isDir) => isDir && (rel === 'scripts' || rel === 'i18n'),
|
||||
/\.md$/,
|
||||
/^node_modules$/,
|
||||
/^diagnose\.js$/,
|
||||
/^test-sidepanel\.html$/,
|
||||
/^memento-web-clipper-chrome-store\.zip$/,
|
||||
/^memento-web-clipper-firefox\.xpi$/,
|
||||
])
|
||||
|
||||
log('⚙️ Processing sidepanel.js...', 'blue')
|
||||
const sidepanelPath = path.join(distDir, 'sidepanel.js')
|
||||
let sidepanelContent = fs.readFileSync(sidepanelPath, 'utf8')
|
||||
sidepanelContent = processSidepanelJs(sidepanelContent)
|
||||
fs.writeFileSync(sidepanelPath, sidepanelContent)
|
||||
log(' ✓ Set ALLOW_INSTANCE_CONFIG = false', 'green')
|
||||
|
||||
log('⚙️ Processing manifest.json...', 'blue')
|
||||
const manifestPath = path.join(distDir, 'manifest.json')
|
||||
let manifestContent = fs.readFileSync(manifestPath, 'utf8')
|
||||
manifestContent = processManifestJson(manifestContent)
|
||||
|
||||
const manifest = JSON.parse(manifestContent)
|
||||
manifest.icons = {
|
||||
"16": "icon-16.png",
|
||||
"48": "icon-48.png",
|
||||
"128": "icon-128.png"
|
||||
}
|
||||
// Firefox uses browser_action icon OR sidebar_action icon
|
||||
manifest.action = {
|
||||
...manifest.action,
|
||||
"default_icon": {
|
||||
"16": "icon-16.png",
|
||||
"48": "icon-48.png",
|
||||
"128": "icon-128.png"
|
||||
}
|
||||
}
|
||||
if (manifest.sidebar_action) {
|
||||
manifest.sidebar_action.default_icon = {
|
||||
"16": "icon-16.png",
|
||||
"48": "icon-48.png",
|
||||
"128": "icon-128.png"
|
||||
}
|
||||
}
|
||||
fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2))
|
||||
log(' ✓ Removed side_panel (Chrome-only)', 'green')
|
||||
log(' ✓ Restricted host_permissions to memento-note.com', 'green')
|
||||
log(' ✓ Kept sidebar_action + browser_specific_settings for Firefox', 'green')
|
||||
log(' ✓ Added icon definitions', 'green')
|
||||
|
||||
await generateIcons()
|
||||
await createXpiPackage()
|
||||
|
||||
log('')
|
||||
log('✅ Build completed successfully!', 'green')
|
||||
log('')
|
||||
log('📦 Output files:', 'blue')
|
||||
log(` • Package: ${path.join(extRoot, 'memento-web-clipper-firefox.xpi')}`, 'reset')
|
||||
log(` • Dist dir: ${distDir}`, 'reset')
|
||||
log('')
|
||||
log('📝 Next steps:', 'blue')
|
||||
log(' 1. Test: web-ext run --source-dir ' + distDir, 'reset')
|
||||
log(' 2. Sign: web-ext sign --api-key=<KEY> --api-secret=<SECRET> --source-dir ' + distDir, 'reset')
|
||||
log(' 3. Submit signed .xpi to https://addons.mozilla.org/developers/', 'reset')
|
||||
log('')
|
||||
} catch (error) {
|
||||
log('')
|
||||
log('❌ Build failed!', 'red')
|
||||
log(` Error: ${error.message}`, 'red')
|
||||
process.exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
build()
|
||||
@@ -3,6 +3,12 @@ const ALLOW_INSTANCE_CONFIG = true
|
||||
const DEFAULT_BASE = 'https://memento-note.com'
|
||||
const STORAGE_KEYS = { baseUrl: 'memento_clipper_base_url', notebookId: 'memento_clipper_notebook_id' }
|
||||
|
||||
/** Compatibilité Chrome/Firefox : Firefox expose l'API sous `browser.*`,
|
||||
* Chrome sous `chrome.*`. On normalise pour utiliser `chrome.*` partout dans le code. */
|
||||
if (typeof chrome === 'undefined' && typeof browser !== 'undefined') {
|
||||
globalThis.chrome = browser
|
||||
}
|
||||
|
||||
let state = 'idle'
|
||||
let notebooks = []
|
||||
let selectedNotebookId = ''
|
||||
|
||||
@@ -3326,9 +3326,9 @@
|
||||
"link0": "الخصوصية",
|
||||
"link1": "الشروط",
|
||||
"link2": "ملفات تعريف الارتباط",
|
||||
"link0Href": "#",
|
||||
"link1Href": "#",
|
||||
"link2Href": "#"
|
||||
"link0Href": "/privacy",
|
||||
"link1Href": "/privacy",
|
||||
"link2Href": "/privacy"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -3326,9 +3326,9 @@
|
||||
"link0": "Datenschutz",
|
||||
"link1": "Bedingungen",
|
||||
"link2": "Cookies",
|
||||
"link0Href": "#",
|
||||
"link1Href": "#",
|
||||
"link2Href": "#"
|
||||
"link0Href": "/privacy",
|
||||
"link1Href": "/privacy",
|
||||
"link2Href": "/privacy"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -1277,7 +1277,17 @@
|
||||
"under": "Under",
|
||||
"trashCascadeWarning": "This will also delete all notes and sub-notebooks.",
|
||||
"trashConfirm": "Move this notebook to trash?",
|
||||
"trashTitle": "Move to trash"
|
||||
"trashTitle": "Move to trash",
|
||||
"slides": "Presentation",
|
||||
"slidesDescription": "Generate a slide deck from all notes in this notebook. Images are automatically included.",
|
||||
"slidesGenerate": "Generate presentation",
|
||||
"slidesGenerating": "Generating presentation from notes...",
|
||||
"slidesGeneratingHint": "This may take 30-60 seconds (outline + per-slide writing)",
|
||||
"slidesReady": "Presentation generated",
|
||||
"slidesStatus1": "Analyzing notebook notes…",
|
||||
"slidesStatus2": "Building presentation outline…",
|
||||
"slidesStatus3": "Writing slides…",
|
||||
"slidesStatus4": "Finalizing layout…"
|
||||
},
|
||||
"notebookSuggestion": {
|
||||
"title": "Move to {name}?",
|
||||
@@ -3593,9 +3603,9 @@
|
||||
"link0": "Privacy",
|
||||
"link1": "Terms",
|
||||
"link2": "Cookies",
|
||||
"link0Href": "#",
|
||||
"link1Href": "#",
|
||||
"link2Href": "#"
|
||||
"link0Href": "/privacy",
|
||||
"link1Href": "/privacy",
|
||||
"link2Href": "/privacy"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -3326,9 +3326,9 @@
|
||||
"link0": "Privacidad",
|
||||
"link1": "Términos",
|
||||
"link2": "Cookies",
|
||||
"link0Href": "#",
|
||||
"link1Href": "#",
|
||||
"link2Href": "#"
|
||||
"link0Href": "/privacy",
|
||||
"link1Href": "/privacy",
|
||||
"link2Href": "/privacy"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -3326,9 +3326,9 @@
|
||||
"link0": "حریم خصوصی",
|
||||
"link1": "شرایط",
|
||||
"link2": "کوکیها",
|
||||
"link0Href": "#",
|
||||
"link1Href": "#",
|
||||
"link2Href": "#"
|
||||
"link0Href": "/privacy",
|
||||
"link1Href": "/privacy",
|
||||
"link2Href": "/privacy"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -1283,7 +1283,17 @@
|
||||
"under": "Sous",
|
||||
"trashCascadeWarning": "Cela supprimera également toutes les notes et sous-carnets.",
|
||||
"trashConfirm": "Déplacer ce carnet vers la corbeille ?",
|
||||
"trashTitle": "Déplacer vers la corbeille"
|
||||
"trashTitle": "Déplacer vers la corbeille",
|
||||
"slides": "Présentation",
|
||||
"slidesDescription": "Génère une présentation à partir des notes du carnet. Les images sont automatiquement intégrées.",
|
||||
"slidesGenerate": "Générer la présentation",
|
||||
"slidesGenerating": "Génération de la présentation à partir des notes...",
|
||||
"slidesGeneratingHint": "Cela peut prendre 30 à 60 secondes (outline + rédaction slide par slide)",
|
||||
"slidesReady": "Présentation générée",
|
||||
"slidesStatus1": "Analyse des notes du carnet…",
|
||||
"slidesStatus2": "Création du plan de la présentation…",
|
||||
"slidesStatus3": "Rédaction des slides…",
|
||||
"slidesStatus4": "Finalisation et mise en page…"
|
||||
},
|
||||
"notebookSuggestion": {
|
||||
"title": "Déplacer vers {name} ?",
|
||||
@@ -3599,9 +3609,9 @@
|
||||
"link0": "Confidentialité",
|
||||
"link1": "Conditions",
|
||||
"link2": "Cookies",
|
||||
"link0Href": "#",
|
||||
"link1Href": "#",
|
||||
"link2Href": "#"
|
||||
"link0Href": "/privacy",
|
||||
"link1Href": "/privacy",
|
||||
"link2Href": "/privacy"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -3326,9 +3326,9 @@
|
||||
"link0": "गोपनीयता",
|
||||
"link1": "शर्तें",
|
||||
"link2": "कुकीज़",
|
||||
"link0Href": "#",
|
||||
"link1Href": "#",
|
||||
"link2Href": "#"
|
||||
"link0Href": "/privacy",
|
||||
"link1Href": "/privacy",
|
||||
"link2Href": "/privacy"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -3326,9 +3326,9 @@
|
||||
"link0": "Privacy",
|
||||
"link1": "Termini",
|
||||
"link2": "Cookie",
|
||||
"link0Href": "#",
|
||||
"link1Href": "#",
|
||||
"link2Href": "#"
|
||||
"link0Href": "/privacy",
|
||||
"link1Href": "/privacy",
|
||||
"link2Href": "/privacy"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -3326,9 +3326,9 @@
|
||||
"link0": "プライバシー",
|
||||
"link1": "利用規約",
|
||||
"link2": "Cookie",
|
||||
"link0Href": "#",
|
||||
"link1Href": "#",
|
||||
"link2Href": "#"
|
||||
"link0Href": "/privacy",
|
||||
"link1Href": "/privacy",
|
||||
"link2Href": "/privacy"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -3326,9 +3326,9 @@
|
||||
"link0": "개인정보",
|
||||
"link1": "약관",
|
||||
"link2": "쿠키",
|
||||
"link0Href": "#",
|
||||
"link1Href": "#",
|
||||
"link2Href": "#"
|
||||
"link0Href": "/privacy",
|
||||
"link1Href": "/privacy",
|
||||
"link2Href": "/privacy"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -3326,9 +3326,9 @@
|
||||
"link0": "Privacy",
|
||||
"link1": "Voorwaarden",
|
||||
"link2": "Cookies",
|
||||
"link0Href": "#",
|
||||
"link1Href": "#",
|
||||
"link2Href": "#"
|
||||
"link0Href": "/privacy",
|
||||
"link1Href": "/privacy",
|
||||
"link2Href": "/privacy"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -3326,9 +3326,9 @@
|
||||
"link0": "Prywatność",
|
||||
"link1": "Regulamin",
|
||||
"link2": "Cookies",
|
||||
"link0Href": "#",
|
||||
"link1Href": "#",
|
||||
"link2Href": "#"
|
||||
"link0Href": "/privacy",
|
||||
"link1Href": "/privacy",
|
||||
"link2Href": "/privacy"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -3326,9 +3326,9 @@
|
||||
"link0": "Privacidade",
|
||||
"link1": "Termos",
|
||||
"link2": "Cookies",
|
||||
"link0Href": "#",
|
||||
"link1Href": "#",
|
||||
"link2Href": "#"
|
||||
"link0Href": "/privacy",
|
||||
"link1Href": "/privacy",
|
||||
"link2Href": "/privacy"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -3326,9 +3326,9 @@
|
||||
"link0": "Конфиденциальность",
|
||||
"link1": "Условия",
|
||||
"link2": "Cookies",
|
||||
"link0Href": "#",
|
||||
"link1Href": "#",
|
||||
"link2Href": "#"
|
||||
"link0Href": "/privacy",
|
||||
"link1Href": "/privacy",
|
||||
"link2Href": "/privacy"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -3326,9 +3326,9 @@
|
||||
"link0": "隐私",
|
||||
"link1": "条款",
|
||||
"link2": "Cookies",
|
||||
"link0Href": "#",
|
||||
"link1Href": "#",
|
||||
"link2Href": "#"
|
||||
"link0Href": "/privacy",
|
||||
"link1Href": "/privacy",
|
||||
"link2Href": "/privacy"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user