/* ============================================================================ CléPass v3 — vue principale (portage fidèle du template de « CléPass Pastel.dc.html »). Reçoit `v` = renderVals() de app.jsx. Conventions de portage : {{ x }} → {v.x} sc-if value="{{ c }}" → {v.c && <>…} sc-for list as x → {v.list.map((x, i) => …)} style="…" → style={sx('…')} style-hover / style-focus→ ============================================================================ */ (function () { const { sx, Hv } = window.CP; const F = React.Fragment; window.CPAppView = function (v) { const Editeur = window.CPEditeur; return (
{/* ===================== EN-TÊTE ===================== */}
{/* Ouvre le tiroir des filtres / dossiers : en mobile la barre latérale n'est plus une colonne, il n'existe aucun autre accès. */} {v.mobile &&
}
CléPassCléPass
{/* Bascule Pro/Perso : retirée en mobile faute de place (elle reste atteignable en tournant l'appareil ou sur un écran plus large). */} {!v.mobile &&
{v.iCase}{v.proLabel}{v.espPro.hovered && {v.iEdit}}
{v.iUser}Perso
}
{/* ── Annuler / Refaire + historique des 10 dernières actions ── Masqués en mobile : trois boutons de plus dans un en-tête déjà saturé, pour des gestes qui relèvent du poste de travail. */} {!v.mobile &&
{v.histOpen &&
{v.histRedoRows.length > 0 &&
À REFAIRE
{v.histRedoRows.map((h, i) => ( {h.label} {h.detail && {h.detail.split('\n').map((ln, j) => { const ci = ln.indexOf(' : '); return ({ci > 0 ? {ln.slice(0, ci)}{ln.slice(ci)} : ln}); })}} {h.time} ))}
}
DERNIÈRES ACTIONS
{v.histRows.map((h, i) => ( {h.label} {h.detail && {h.detail.split('\n').map((ln, j) => { const ci = ln.indexOf(' : '); return ({ci > 0 ? {ln.slice(0, ci)}{ln.slice(ci)} : ln}); })}} {h.time} ))} {!v.histRows.length &&
Aucune action historisée pour l'instant.
} {v.histRows.length > 0 &&
Cliquer une action l'annule ainsi que toutes celles qui ont suivi.
}
}
} {/* ── Menu d'actions du mobile ────────────────────────────────────── Recueille ce que l'en-tête d'un téléphone ne peut pas afficher : la bascule Pro/Perso (et son renommage) et l'annulation. Sans lui, ces fonctions n'étaient atteignables sur AUCUN petit écran. */} {v.mobile &&
{v.menuActions.open &&
ESPACE
{v.iCase}{v.menuActions.proLabel}
{v.iUser}Perso
Renommer l’espace pro
HISTORIQUE
{/* Les libellés portent déjà l'action concernée (« Annuler : Création de la fiche … »), ou « Rien à annuler » : inutile de doubler d'une infobulle, invisible au doigt. */} {v.histUndoTitle} {v.histRedoTitle}
}
}
{!v.mobile && v.compteEmail}{v.iLock}
{v.menuOpen &&
{v.iUser}
{v.compteNom}
{v.compteEmail}
{v.iLock}
Déconnecter le coffre
Verrouille cet appareil
{v.iPower}
Déconnecter de tous les périphériques
Verrouillage panique · ferme partout
{/* Extender « historique de connexion » : les dernières ouvertures du coffre (appareil, IP, lieu, navigateur, date). */}
Voir l'historique de connexion {v.iChev} {v.menuSessOpen &&
{v.menuSessLoading &&
Chargement des connexions…
} {v.menuSessEmpty &&
Aucune connexion récente.
} {v.menuSessions.map((s, i) => (
{v.iGlobe}
{s.nom} {s.courante && Cet appareil} {s.multi && × {s.nb}} {s.date}
{s.ip} · {s.lieu}
{s.ua &&
{s.ua}
}
))}
}
}
{/* ============ BANDEAU « ALERTE CONNEXION INHABITUELLE » ============ Levé au déverrouillage quand la session courante vient d'un appareil ou d'un lieu jamais vus (réglage « Sécurité & outils »). */} {v.alerteCx &&
{v.iGlobe}
Connexion inhabituelle détectée
{v.alerteCx.motifs.map((m) => (
{m}
))}
Si ce n’est pas vous, déconnectez toutes les sessions et changez votre mot de passe maître.
Déconnecter partout
}
{/* Voile du tiroir : le refermer d'un tap hors de la barre est le seul geste naturel sur téléphone. */} {v.barreOuverte &&
} {/* ===================== BARRE LATÉRALE ===================== En mobile elle devient un tiroir posé par-dessus le contenu, et n'est montée que lorsqu'il est ouvert. Elle GARDE display:flex + une hauteur définie (top/bottom) : les margin-top:auto de la carte Extension et de la rangée du bas en dépendent. */} {(!v.mobile || v.barreOuverte) &&
Filtres {v.iEdit}
{v.catAll.icon}
{v.catAll.label}{v.catAll.count}
{v.catFav.icon}
{v.catFav.count}
{v.catShared.icon}
{v.catShared.count}
{v.catsGrid.map((cat, i) => (
{cat.icon}
{cat.label}{cat.count}
))}
{v.folderRootDrop.active ? 'Dossiers · racine' : 'Dossiers'} +
{v.folders.map((folder) => (
{v.iChev} {folder.icon} {folder.editing && } {folder.notEditing && {folder.name}{folder.count}+}
))} {/* Carte « Extension Chrome » : proposée tant que l'extension n'est pas détectée ; un clic déplie les étapes d'installation. Elle porte le margin-top:auto quand elle est visible (sinon la rangée du bas le garde) — deux marges auto se partageraient l'espace libre. */} {v.extPromo &&
{v.extPromo.icone}
Extension Chrome
Remplissage auto sur les sites
{v.extPromo.ouvert ? 'Fermer' : 'Installer'}
{v.extPromo.ouvert &&
1. Ouvrez chrome://extensions
2. Activez le Mode développeur
3. « Charger l’extension non empaquetée »
→ dossier extension/ de CléPass.
}
}
{v.santeBack &&
{v.iChev}
Retour
} {v.santeNormal &&
{v.sante.score}
{v.sante.tileAlerte && {v.sante.tileAlertes}}
Santé
}
{v.toolsBack &&
{v.iChev}
Retour
} {v.toolsNormal &&
{v.iSliders}
Outils
}
{v.settingsBack &&
{v.iChev}
Retour
} {v.settingsNormal &&
{v.iSettings}
Paramètres
}
} {/* ===================== LISTE ===================== */} {v.showList &&
{/* Rail alphabétique : 27 cibles de 17 px, impossibles à viser au doigt et 26 px pris sur une largeur déjà comptée — retiré en mobile. */} {!v.mobile &&
{v.alphabet.map((a) => (
{a.label}
))}
}
{v.title} · {v.count}
{v.iPlus}
{v.iSearch}
{/* Invitations de partage : rien n'entre dans le coffre sans accord. */} {v.invitations.rows.length > 0 &&
{v.invitations.err &&
{v.invitations.err}
} {v.invitations.rows.map((r) => (
{r.de} souhaite partager une fiche avec vous.
{r.quoi}
{r.busy ? '…' : 'Accepter'} Refuser
))}
}
{v.items.map((item) => (
{item.avEl}
{item.name}
{item.sub}
{item.shared ? {v.iShare} {item.shareCount} : null}
))} {v.leavingList.map((g, i) => (
{g.av}
{g.name}
{g.sub}
))} {v.isEmpty &&
{v.iFolder}
{v.emptyMsg}
}
} {v.showDetail &&
{/* Retour en mobile : le détail occupe seul l'écran, sans ce bandeau on ne peut plus revenir à la liste. Collant, car le panneau défile. */} {v.mobileRetour &&
{v.iChev}{v.mobileRetourLabel}
} {/* ===================== VUE ÉLÉMENT ===================== */} {v.isItem &&
{v.editMode &&
{/* Titre / sous-titre : deux champs encadrés collés en un seul bloc (pas d'arrondi ni de double filet entre eux — le sous-titre remonte de la largeur d'une bordure), sans étiquette : le cadre suffit à montrer qu'on peut écrire, là où la lecture n'affiche que du texte. */}
{v.editAvEl}
{v.editTitleErr &&
Le titre est obligatoire
}
{/* Annuler / Enregistrer : à droite du titre / sous-titre de la fiche. */}
Annuler
Enregistrer
{/* Style d'avatar (switch Lettre/Logo/Icône) + couleur : posé à même le panneau (ni fond, ni cadre, ni retrait), simplement titré comme « SITES WEB » ou « TAGS INTELLIGENTS ». Le switch reste en haut à gauche, la 1ʳᵉ ligne de couleurs à sa suite, les suivantes dessous. */}
Style et couleur
{v.editModes.map((m) => (
{m.label}
))}
{v.editColors.map((c, i) => (c.sep ? : {c.icon}{c.dotStyle ? : null}))}
{v.editShowIcons &&
{v.editIconOptions.map((o) => (
{o.icon}
))}
} {/* Choix du logo : posé à même le panneau lui aussi, comme « Style et couleur » — seul l'intitulé sépare la rangée de logos du reste. */} {v.editShowLogos &&
Choisir le logo {v.editLogoOptions}
}
Sites web
{v.editUrlChips.map((u, i) => ({u.label}))}
Tags intelligents
{v.editTagChips.map((tg, i) => ({tg.label}))}
{v.editBlocks.map((b) => (
{b.showBefore &&
}
{b.isPwIcon && } {b.isO365 && } {b.isOtp && } {b.isWifi && } {b.isCard && } {b.isNotes && } {b.isCat && b.catIcon} {b.name}
{b.bodyOpen && {b.isLogin &&
Nom d'utilisateur
Mot de passe
{v.iEye}{v.iRefresh}
} {b.isOtp &&
Émetteur
Clé secrète (OTP)
{b.otpLive &&
{b.otpLiveRemaining}
{b.otpLiveCode} code actuel
}
} {b.isWifi &&
SSID
Mot de passe
Sécurité
} {b.isCard &&
Numéro de carte
Expiration
CVV
} {b.isNotes &&
Contenu
} {b.isCat &&
{b.catFields.map((f, fi) => (
{f.isTexte &&
{f.label}
} {f.isDate &&
{f.label}
} {f.isNombre &&
{f.label}
} {f.isMulti &&
{f.label}
} {f.isMdp &&
{f.label}
{v.iEye}
} {f.isListe &&
{f.label}
{f.value}
{v.iChev}
{f.menuOpen &&
{f.options.map((o) => (
{o.label}{o.selected && }
))}
}
} {f.isToggle &&
{f.label}
{f.toggleLabel}
}
))}
}
}
{b.showAfter &&
}
))}
{v.showEnd &&
}
+ Ajouter un élément
{/* Les champs de type personnalisé sont désormais édités dans leur bloc (type 'cat:…'), déplaçable comme les autres. */} {v.editAddOpen &&
CHOISIR UN TYPE
{v.editAddOptions.map((o) => ( {o.isPw && } {o.isOtp && } {o.isWifi && } {o.isCard && } {o.isNotes && }
{o.label}
{o.desc}
))} {v.editAddCustom.length > 0 &&
CHOISIR UN TYPE PERSONNALISÉ
{v.editAddCustom.map((o) => ( {o.icon}
{o.label}
{o.desc}
))}
}
}
} {v.notEditMode &&
{v.sel.av}
{v.sel.name}{v.sel.verified && {v.iShieldCheck}}
{v.sel.sub}{v.sel.partageDe ? · partagé par {v.sel.partageDe} ({v.sel.partagePermLabel}) : null}
{/* flex-wrap en mobile : sans lui, « Modifier » et le menu ⋮ sont poussés HORS de l'écran par les onglets — donc inatteignables. */}
Détail
Activité
{v.tabPartage.visible &&
Partager{v.sel.shared ? {v.sel.shareCount} : null}
}
{!v.sel.lectureSeule &&
{v.iEdit} Modifier
} {v.sel.lectureSeule && {v.iEye} Lecture seule}
{v.iDots}
{v.detailMenuOpen &&
{/* « Modifier popup » ouvre CPEditeur (editeur.jsx), dont la mise en page est figée à deux colonnes (216 px de barre latérale non rétractable) : à 375 px la colonne de contenu tombe sous 90 px. L'entrée est donc retirée en mobile — « Modifier » édite la même fiche dans le panneau, lui responsive. */} {!v.sel.lectureSeule && !v.mobile && {v.iEdit}Modifier popup} {v.iStar}{v.favLabel} {v.iTrash}Supprimer
}
{/* ---- Onglet Coffre ---- */} {v.showCoffre && {/* UNE section par bloc de la fiche, dans l'ordre défini en édition — plusieurs blocs d'un même type = sections numérotées. */} {v.sel.sections.map((sec, si) => ( {sec.otp &&
{sec.otp.title}
{v.iShield} {sec.otp.label}
{sec.otp.code}
{sec.otp.remaining}
{v.iCopy} Copier
} {sec.login &&
{sec.login.title}
Nom d'utilisateur
{sec.login.user}
{v.iCopy}
Mot de passe
{sec.login.pwEl}
{sec.login.forceFaible ? v.iRadar : v.iShieldCheck}{v.iEye}{v.iCopy}
{/* Le renouvellement automatique n'est pas implémenté : la tuile affichait un interrupteur figé sur ON et une échéance inventée (« prochaine dans 28 jours »). Elle annonce désormais la fonction sans prétendre l'assurer. */} {sec.login.rotation &&
{v.iRefresh}
Rotation automatique
Renouvellement périodique du mot de passe
Coming soon
}
} {sec.wifi &&
{sec.wifi.title}
{sec.wifi.qr &&
{sec.wifi.qrEl}
Connexion automatique
Scannez ce QR code avec l'appareil photo d'un iPhone ou d'un Android pour rejoindre {sec.wifi.ssid} sans saisir le mot de passe.
{sec.wifi.payload}
}
SSID
{sec.wifi.ssid}
Sécurité
{sec.wifi.security}
Mot de passe
{sec.wifi.passEl}
{v.iEye}{v.iCopy}
} {sec.card &&
{sec.card.title}
Numéro de carte
{sec.card.number}
{v.iEye}{v.iCopy}
Expiration
{sec.card.exp}
CVV
{sec.card.cvv}
{v.iEye}
} {sec.note &&
{sec.note.title}
{sec.note.text}
} {/* Champs des types personnalisés (popup « Personnaliser le type ») */} {sec.cat &&
{sec.cat.title}
{sec.cat.fields.map((fld, fi) => (
{fld.label}
{fld.isMdp &&
{fld.value}
} {(fld.isTexte || fld.isListe || fld.isToggle || fld.isDate || fld.isNombre) &&
{fld.value}
} {fld.isLien &&
{fld.value}
} {fld.isMulti &&
{fld.value}
}
{fld.isMdp && fld.hasVal && {v.iEye}} {fld.isMdp && fld.hasVal && {v.iCopy}} {fld.isToggle &&
}
))}
}
))}
Tags intelligents{v.sel.tags.map((tag, i) => ({tag.label}))}
} {/* ---- Onglet Activité : historique fusionné (modifications + accès), toutes les entrées avec le même rendu (icône + libellé en gras). ---- */} {v.showActivite &&
{v.activite.map((h, i) => (
{h.iconEl} {h.label} {h.detail && {h.detail.split('\n').map((ln, j) => { const ci = ln.indexOf(' : '); return ({ci > 0 ? {ln.slice(0, ci)}{ln.slice(ci)} : ln}); })}} {h.time}
))} {v.activite.length === 0 &&
Aucune activité enregistrée pour cette fiche.
}
{v.iRefresh}
Versions restaurables
Revenez à n'importe quelle version antérieure du mot de passe.
Restaurer
} {/* ---- Onglet Partager (bout en bout) ---- */} {v.showPartage && {v.share.dejaRecu ? (
Cette entrée vous a été partagée : seul son propriétaire peut la partager.
) : (
Partager avec un membre
La clé de l'entrée est scellée pour le destinataire dans votre navigateur — le serveur ne peut pas la lire.
{v.share.occupe ? 'Scellement…' : 'Partager'}
v.share.onPermission('lecture')} style={sx(`flex:1;text-align:center;padding:8px 6px;border-radius:8px;font-size:12px;font-weight:600;cursor:pointer;${v.share.permission === 'lecture' ? 'background:#6b4fb8;color:#fff;' : 'color:#7a7184;'}`)}>Lecture seule
v.share.onPermission('lecture_ecriture')} style={sx(`flex:1;text-align:center;padding:8px 6px;border-radius:8px;font-size:12px;font-weight:600;cursor:pointer;${v.share.permission === 'lecture_ecriture' ? 'background:#6b4fb8;color:#fff;' : 'color:#7a7184;'}`)}>Lecture + écriture
{v.share.err &&
{v.share.err}
} {v.share.envoye &&
Partage envoyé — il apparaîtra dans le coffre du destinataire à sa prochaine connexion.
}
)}
Partages en cours
{v.share.liste.length === 0 ?
Cette entrée n'est partagée avec personne pour l'instant.
: v.share.liste.map((p, i) => (
{p.destinataire} {p.permission} {p.statut} {!v.share.dejaRecu && (p.id ? {v.iDots} {p.menuOpen &&
DROIT ACCORDÉ
{p.ecriture ? '' : '✓'} Lecture seule {p.ecriture ? '✓' : ''} Lecture + écriture
v.share.onRevoke(p.id)} style="display:flex;align-items:center;gap:9px;padding:8px 9px;border-radius:9px;cursor:pointer" hover="background:#fdf1f1"> {v.iTrash} Révoquer le partage
}
: )}
))}
}
}
} {/* ===================== VUE SANTÉ ===================== */} {v.isHealth &&
Santé du coffre
{v.sante.sousTitre}
{v.sante.score}/ 100
{v.sante.niveau}
{v.sante.resume}
{v.sante.nbFaibles}
mots de passe faibles
{v.sante.nbReutil}
réutilisés
{v.sante.nb2fa}
protégés en 2FA
{v.sante.rien &&
Aucun mot de passe faible ni réutilisé dans ce coffre.
} {v.sante.faibles.length > 0 &&
Mots de passe à renforcer
{v.sante.faibles.map((r) => (
{r.ini}
{r.nom}
{r.sousTitre}
Corriger
))}
} {v.sante.reutilises.length > 0 &&
Mots de passe réutilisés
{v.sante.reutilises.map((r) => (
{r.ini}
{r.nom}
{r.sousTitre}
Corriger
))}
}
{v.iRadar}
Surveillance des fuites
{v.sante.fuEtat === 'ok' && {v.sante.fuNb === '0' ? 'aucun exposé' : v.sante.fuNb + ' exposé(s)'}}
Vérifie vos mots de passe auprès de Have I Been Pwned. L'empreinte est calculée sur votre appareil et seuls ses 5 premiers caractères sont envoyés (k-anonymity) : ni le mot de passe, ni son empreinte complète, ni le nom de la fiche ne sortent du navigateur.
{v.sante.fuEtat === 'scan' ? 'Vérification…' : (v.sante.fuEtat === 'idle' ? 'Vérifier maintenant' : 'Relancer la vérification')}
{v.sante.fuQuand && Dernière vérification : {v.sante.fuQuand}}
{v.sante.fuErr &&
{v.sante.fuErr}
}
{v.sante.fuRows.map((r) => (
{r.ini}
{r.nom}
{r.sousTitre}
Corriger
))}
{v.iLock}
Chiffré de bout en bout
AES-256-GCM · Argon2id · Zero-knowledge
{v.iShieldCheck} Actif
} {/* ===================== VUE SÉCURITÉ & OUTILS ===================== */} {v.isTools &&
Sécurité & outils
Réglages de protection et fonctions avancées du coffre
{v.toolSections.map((sec) => (
{sec.title}
{sec.tools.map((t) => (
{t.icon}
{t.title}
{t.desc}
{t.soon && Coming soon} {t.isToggle &&
} {t.isBadge && {t.badge}} {t.isChevron && {v.iChev}}
{t.open &&
{t.detail}
{t.cta}
}
))}
))}
} {/* ===================== VUE PARAMÈTRES ===================== */} {v.isSettings &&
Paramètres
Préférences pour ce coffre. Stockées chiffrées comme le reste.
{v.iUser}
{v.compteNom}
{v.compteEmail} {/* Sans ce badge, une adresse non confirmée s'affiche comme n'importe quelle autre et laisse croire que tout est en règle — alors que le partage est bloqué. */} {!v.compteEmailVerifie && Non confirmée}
{/* Bouton TOUJOURS présent : le masquer quand le serveur n'envoie pas de courriel laissait croire que changer d'adresse était impossible dans CléPass, alors que seule la configuration du serveur manquait. La popup explique le cas. Le badge « Coming soon » ne vit QUE sur le bouton d'envoi : ici il annoncerait une porte fermée alors que la popup s'ouvre et se parcourt normalement. */} {v.iGlobe} Adresse e-mail {v.iLock} Mot de passe maître
{v.mp.saved &&
{v.iShieldCheck}Mot de passe maître modifié. Vos données ont été rechiffrées avec la nouvelle clé.
} {/* Changement d'adresse déposé mais pas confirmé : sans ce rappel, l'utilisateur qui n'a jamais reçu le courriel n'a aucun moyen de savoir qu'une demande est en cours, ni de la reprendre. */} {v.courriel.enAttente &&
En attente de confirmation : {v.courriel.enAttente}. Votre compte reste rattaché à l’adresse actuelle tant que le lien n’est pas ouvert.
Annuler
}
Avatar par défaut
{v.avatarDefaultSeg.map((o) => (
{o.label}
{o.showChevron &&
{v.iChev}
}
{o.menuOpen &&
{v.iLayers}
Appliquer à tous les mots de passe du coffre
Remplace l'avatar de chaque entrée par ce type. Action ponctuelle.
}
))}
{v.showLogoWarn &&
Le logo est récupéré depuis icons.duckduckgo.com à chaque affichage. Cela révèle au CDN les domaines de votre coffre.
} {v.avatarApplied &&
{v.iShieldCheck}Avatar appliqué à toutes les fiches du coffre.
}
Comportement de la sidebar
{v.sidebarToggles.map((s) => (
{s.label}
{s.hasDesc &&
{s.desc}
}
{s.soon ? Coming soon :
}
))}
Comportement de la liste des fiches
{v.listToggles.map((s) => (
{s.label}
{s.hasDesc &&
{s.desc}
}
{s.soon ? Coming soon :
}
))}
Sécurité
Verrouillage automatique
Verrouille le coffre après inactivité
{v.autoLock} {v.iChev}
{v.lockMenuOpen &&
{v.lockOptions.map((o) => (
{o.label}{o.selected && }
))}
}
{v.secToggles.map((s) => (
{s.label}
{s.hasDesc &&
{s.desc}
}
{s.soon ? Coming soon :
}
))}
Préférences
Thème
{/* flex-wrap : quatre thèmes sur deux rangées (la tuile fait la moitié de la grille des Préférences). */}
{v.themeSeg.map((o) => (
{o.label} {o.beta && {o.badge}}
))}
Langue
Langue de l'interface
Français {v.iChev}
Zone de danger
Supprimer toutes les données
Efface mots de passe, codes 2FA, Wi-Fi et notes. Le coffre reste utilisable, les dossiers sont réinitialisés.
{v.iTrash} Tout supprimer…
{/* Suppression du COMPTE (RGPD) — à ne pas confondre avec la vidange du coffre ci-dessus : ici, plus rien ne subsiste, pas même l'adresse e-mail. */}
Supprimer définitivement mon compte
Efface le compte, le coffre, l'historique, les partages et les appareils. Irréversible : aucune sauvegarde ne pourra les restaurer.
{v.iTrash} Supprimer mon compte…
}
}
{/* ===================== TOAST « JEU DE DÉMONSTRATION » ===================== */} {v.demoToast > 0 &&
{v.iShieldCheck} {v.demoToast} entrées factices générées dans le dossier « DEMO ».
} {/* ===================== OVERLAY DÉCONNEXION / PANIQUE ===================== */} {v.lockShown &&
{/* width:390px sans max-width débordait à 375 px — sur l'écran même où l'on vient de verrouiller son coffre. */}
{v.lockIcon}
{v.lockTitle}
{v.lockText}
{v.iLock} {v.lockBtn}
{/* Extender « dernières connexions » : nom, IP, lieu, user-agent, date. */}
Voir les dernières connexions {v.iChev} {v.lockSessOpen &&
{v.lockSessLoading &&
Chargement des connexions…
} {v.lockSessEmpty &&
Aucune connexion récente.
} {v.lockSessions.map((s, i) => (
{v.iGlobe}
{s.nom} {s.courante && Cet appareil} {s.multi && × {s.nb}} {s.date}
{s.ip} · {s.lieu}
{s.ua &&
{s.ua}
}
))}
}
} {/* « Partager » est désormais un onglet du panneau détail (voir showPartage), plus un popup modal. */} {/* Bilan d'import (toast) */} {v.importBilan &&
{v.iShieldCheck} {v.importBilan.crees} fiche(s) importée(s){v.importBilan.doublons ? ' · ' + v.importBilan.doublons + ' doublon(s) ignoré(s)' : ''}
} {/* ===================== CONFIRMATION DE SUPPRESSION ===================== */} {v.supprConfirm &&
{v.iTrash}
{v.supprConfirm.partageDe ? 'Quitter le partage ?' : 'Supprimer « ' + v.supprConfirm.nom + ' » ?'}
{v.supprConfirm.partageDe ?
Cette fiche appartient à {v.supprConfirm.partageDe}. Vous n'en êtes pas propriétaire : la retirer ne la supprime pas de son coffre. Vous quittez simplement le partage et perdez l'accès.
Pour retrouver l'accès, il faudra que le propriétaire vous repartage la fiche.
: {v.supprConfirm.nbPartages > 0 &&
Cette fiche est partagée avec {v.supprConfirm.nbPartages} personne(s), qui en perdront l'accès immédiatement.
}
La fiche part à la corbeille et reste restaurable 30 jours{v.supprConfirm.nbPartages > 0 ? ' — la restaurer rendra aussi l’accès aux destinataires' : ''}.
} {v.supprErr &&
{v.supprErr}
}
Annuler
{v.supprBusy ? '…' : (v.supprConfirm.partageDe ? 'Quitter le partage' : 'Supprimer')}
} {/* ===================== VERSIONS RESTAURABLES ===================== */} {v.versions.open &&
Versions de « {v.versions.nom} »
Les 20 dernières versions enregistrées, déchiffrées sur votre appareil. Restaurer remplace le contenu actuel — qui devient lui-même une version récupérable.
{v.versions.etat === 'charge' &&
Chargement…
} {v.versions.err &&
{v.versions.err}
} {v.versions.etat === 'ok' && v.versions.rows.length === 0 &&
Aucune version antérieure : cette fiche n'a pas encore été modifiée.
}
{v.versions.rows.map((r) => (
{v.iRefresh} {r.quand}{r.recente ? ' · version précédente' : ''} {r.apercu} Restaurer
))}
Fermer
} {/* ===================== PHRASE DE PASSE ===================== */} {v.phrase.open &&
Phrase de passe
Mots tirés au hasard sur votre appareil parmi {v.phrase.taille}, avec un générateur cryptographique. Idéale pour le mot de passe maître : longue à casser, facile à retenir.
{v.phrase.valeur}
{v.phrase.niveau[0]} · environ {v.phrase.bits} bits d'entropie
Nombre de mots
{v.phrase.motsOpts.map((o) => (
{o.label}
))}
Fermer
Regénérer
Copier
} {/* ===================== CORBEILLE ===================== */} {v.corbeille.open &&
Corbeille
Les fiches supprimées restent ici 30 jours, puis sont purgées automatiquement. Elles restent chiffrées pendant tout ce temps.
{v.corbeille.err &&
{v.corbeille.err}
}
{v.corbeille.vide &&
La corbeille est vide.
} {v.corbeille.rows.map((r) => (
{r.ini} {r.nom} {r.sousTitre ? r.sousTitre + ' · ' : ''}{r.date} {r.confirming ? ( Effacer définitivement ? Non {r.busy ? '…' : 'Oui'} ) : ( {r.busy ? 'Restauration…' : 'Restaurer'} {v.iTrash} )}
))}
Fermer
} {/* ===================== POPUP « MODIFIER » (dc-import → CPEditeur) ===================== */} {v.editorOpen && Editeur ? : null} {/* ===================== RENOMMER L'ESPACE ===================== */} {v.renameOpen &&
Renommer l'espace
Le nom de l'onglet professionnel
Annuler
Enregistrer
} {/* ===================== DÉPLACER UN DOSSIER (confirmation) ===================== */} {v.folderMove.open &&
Déplacer le dossier
Confirmez le déplacement dans l'arborescence.
{v.folderMove.srcIcon}
Source
{v.folderMove.srcName}
{v.folderMove.srcPath}
{v.iChev}
{v.folderMove.dstIcon}
Destination
{v.folderMove.dstName}
{v.folderMove.dstPath}
{v.folderMove.note}
Annuler
Déplacer
} {/* ===================== MOT DE PASSE MAÎTRE ===================== */} {v.mp.open &&
{v.iLock}
Mot de passe maître
Il protège l'intégralité du coffre : il n'est jamais stocké, seule une clé dérivée le vérifie.
Mot de passe actuel
Nouveau mot de passe{v.mp.strLabel}
Confirmer le nouveau mot de passe
{v.iEye}Afficher les mots de passe
{v.mp.err &&
{v.mp.err}
}
Annuler
Modifier le mot de passe
} {/* ===================== ADRESSE E-MAIL ===================== L'adresse sert d'identifiant de connexion ET de destinataire des partages : la changer déplace l'identité du compte. D'où le mot de passe maître exigé ici, et le fait que rien ne bouge avant que le lien envoyé à la NOUVELLE adresse ait été ouvert. */} {v.courriel.open &&
{v.iGlobe}
Adresse e-mail
Adresse actuelle : {v.courriel.actuelle}. Un lien de confirmation partira vers la nouvelle : votre compte ne changera d’adresse qu’une fois ce lien ouvert.
{/* Dit AVANT la saisie : sans envoi de courriels, la demande finirait de toute façon en erreur côté serveur, après avoir fait taper l'adresse et le mot de passe maître pour rien. */} {!v.courriel.possible &&
Ce serveur n’envoie pas de courriels : impossible de prouver que la nouvelle adresse est bien la vôtre, et donc de changer d’adresse. Contactez l’administrateur du service.
}
Nouvelle adresse
Mot de passe maître
{v.iEye}Afficher le mot de passe
Votre mot de passe maître n’est pas transmis : il sert ici à prouver que la demande vient bien de vous, et non du seul porteur de cette session.
{v.courriel.err &&
{v.courriel.err}
}
Annuler
{/* Envoi gelé : le bouton reste à sa place pour montrer où aboutit le parcours, mais il ne porte AUCUN gestionnaire de clic — un simple style grisé se contourne au clavier ou à l'inspecteur. */} {v.courriel.envoiActif ?
{v.courriel.busy ? 'Envoi…' : 'Envoyer le lien'}
:
Envoyer le lien {v.courriel.badge}
}
} {/* ===================== SUPPRESSION DU COMPTE ===================== Irréversible et sans filet : le coffre est chiffré avec un mot de passe maître que le serveur n'a jamais eu, donc aucune restauration n'est possible, ni ici ni côté hébergeur. D'où les trois preuves demandées. */} {v.suppCompte.open &&
{v.suppCompte.etape === 'fait' ?
{v.iTrash}
Compte supprimé
Votre compte et l’intégralité de son contenu ont été effacés. Vous allez être redirigé vers l’écran d’accueil.
:
{v.iTrash}
Supprimer définitivement mon compte
{v.suppCompte.email}
Seront effacés : toutes vos fiches, l’historique de leurs versions, la corbeille, vos dossiers, vos partages (émis et reçus), vos appareils et vos préférences. Rien n’est récupérable — vos données sont chiffrées avec un mot de passe maître que le serveur n’a jamais eu.
Pour en garder une copie, fermez cette fenêtre et lancez d’abord « Sauvegarde chiffrée hors-ligne » dans Sécurité & outils.
Mot de passe maître
{v.suppCompte.demandeCode &&
Code 2FA ou code de secours
}
Recopiez SUPPRIMER pour confirmer
{v.suppCompte.err &&
{v.suppCompte.err}
}
Annuler
{v.suppCompte.busy ? 'Suppression…' : 'Supprimer définitivement'}
}
} {/* ===================== 2FA DU COMPTE ===================== Interrupteur « Exiger la 2FA à chaque connexion » : activation réelle (secret + QR otpauth → code de vérification → codes de secours) ou désactivation (code Authenticator ou code de secours). */} {v.tfaPop &&
{v.iShieldCheck}
{v.tfaPop.titre}
{v.tfaPop.etape === 'chargement' &&
Préparation du secret 2FA…
} {v.tfaPop.etape === 'secret' &&
Scannez ce QR code avec votre application Authenticator (ou saisissez la clé à la main), puis recopiez le code à 6 chiffres qu'elle affiche.
{v.tfaPop.qrEl &&
{v.tfaPop.qrEl}
} {v.tfaPop.secret &&
{v.tfaPop.secret}{v.iCopy}
}
Code de vérification
{v.tfaPop.err &&
{v.tfaPop.err}
}
Annuler
{v.tfaPop.busy ? 'Vérification…' : 'Activer la 2FA'}
} {v.tfaPop.etape === 'codes' &&
2FA activée. Conservez ces codes de secours hors de ce coffre : chacun ouvre une session une seule fois, si vous perdez votre téléphone. Ils ne seront plus jamais affichés.
{(v.tfaPop.codes || []).map((c) => (
{c}
))}
Copier les codes
J'ai noté mes codes
} {v.tfaPop.etape === 'desactiver' &&
Saisissez le code à 6 chiffres de votre application Authenticator, ou l'un de vos codes de secours. Vos codes de secours actuels seront invalidés.
{v.tfaPop.err &&
{v.tfaPop.err}
}
Annuler
{v.tfaPop.busy ? 'Vérification…' : 'Désactiver'}
}
} {/* ===================== GÉRER LES TYPES ===================== */} {v.catManagerOpen &&
Gérer les types
Glissez pour réordonner · les types par défaut ne sont pas supprimables
{v.catManagerRows.map((c, i) => (
{c.icon} {c.label} {/* Zone boutons : exclue du drag de la ligne (dragstart bloqué). */} { if (ev) { ev.preventDefault(); ev.stopPropagation(); } }} style={sx('display:flex;align-items:center;gap:2px;flex:none;cursor:default')}> {c.editable && {v.iEdit}} {c.deletable && } {c.notDeletable && }
))}
Ajouter
} {/* ===================== PERSONNALISER LE TYPE ===================== */} {v.catEdit.open &&
{v.catEdit.tileIcon}
Personnaliser le type
⧉ Dupliquer
Icône
{v.catEdit.icons.map((o, i) => (
{o.icon}
))}
Couleur
{v.catEdit.colors.map((o, i) => (
))}
{/* Deux colonnes : champs à gauche, aperçu de la fiche à droite, les deux sections démarrant au même niveau (aperçu sticky au défilement). */}
{v.catEdit.options.length > 0 &&
Options
{v.catEdit.options.map((o, i) => (
{o.label}
))}
} {v.catEdit.hasFields &&
Champs du panneau de détail
{v.iPlus} Ajouter un champ
{v.catEdit.fields.map((f, i) => (
{f.widths.map((wo, k) => (
{wo.label}
))}
{f.isListe &&
{f.optionChips.map((o, k) => ({o.label}))}
}
))} {!v.catEdit.fields.length &&
Aucun champ personnalisé — la fiche garde sa présentation standard.
}
}
Aperçu de la fiche
{v.catEdit.tileIcon}
{v.catEdit.label}
{v.catEdit.hasFields ? {v.catEdit.preview.length > 0 &&
{v.catEdit.preview.map((p, i) => (
{p.label}
{p.value}
))}
} {!v.catEdit.preview.length &&
Ajoutez des champs pour voir l'aperçu.
}
: {v.catEdit.key === 'pw' &&
Nom d'utilisateur
alex@exemple.fr
Mot de passe
••••••••
{v.catEdit.optVals.rotation &&
{v.iRefresh}
Rotation automatique
}
} {v.catEdit.key === 'wifi' && {v.catEdit.optVals.qr &&
{v.iQr}
QR de connexion automatique
}
SSID
MonRéseau
Sécurité
WPA2
} {v.catEdit.key === '2fa' &&
AUTHENTICATOR
123 456
} {v.catEdit.key === 'card' &&
Numéro de carte
•••• •••• •••• 4242
Expiration
08 / 28
CVV
•••
} {v.catEdit.key === 'notes' &&
Texte libre chiffré — vos notes sécurisées s'affichent ici.
}
}
{v.catEdit.hasFields ? "Ces champs s'affichent dans la fiche de chaque élément du type. Réglez la largeur de chacun (1/1, 1/2 ou 1/3) et glissez pour réordonner. Types : texte, mot de passe masqué, liste, interrupteur, lien, date, nombre, texte multiligne." : "Ces options s'appliquent à toutes les fiches de ce type. L'aperçu ci-dessus reflète vos réglages en direct."}
Annuler
Enregistrer
{/* Avertissement avant suppression d'un champ renseigné sur des éléments */} {v.catEdit.confirmDelete &&
Supprimer le champ « {v.catEdit.confirmDelete.label} » ?
Ce champ est renseigné sur {v.catEdit.confirmDelete.countLabel} de ce type. Ces valeurs seront définitivement effacées à l'enregistrement du type.
Annuler
Supprimer le champ
}
} {/* ===================== MENU « NOUVEL ÉLÉMENT » ===================== */} {v.addMenuOpen &&
TYPES GÉNÉRIQUES
{v.addGenericRows.map((a) => (
{a.icon}
{a.name}
{a.desc}
{a.soon && Coming soon}
))}
TYPES PERSONNALISÉS +
{v.addCustomRows.map((a) => (
{a.icon}
{a.name}
{a.desc}
))}
} {/* ===================== OVERLAY ÉDITEUR (ajouter / modifier) ===================== */} {v.edOpen &&
{v.edTitle}
{v.edShowSubtitle &&
Toutes les données sont chiffrées localement avant envoi.
}
Nom (facultatif)
{v.edIsWifi &&
SSID (nom du réseau) *
} {v.edShowDomain &&
{v.edDomainLabel}
}
{v.edShowAvatar &&
{v.avGlyph}
{v.avModes.map((m) => (
{m.label}
))}
{v.swatches.map((c, i) => (
))}
} {v.edIsWifi &&
Mot de passe Wi-Fi
{v.iEye}
{v.wifiStrLabel}
Sécurité
SSID caché
}
Dossier
Favori
Partagé
{v.edIsLogin &&
Type
{v.iPlus}
} {v.edIsLogin &&
Comptes{v.acctCount}
{v.iPlus} Ajouter
{v.acctRows.map((a, i) => (
{a.label} {a.principal && PRINCIPAL} {a.canRemove &&
{v.iTrash}
}
Identifiant
Mot de passe
{v.iEye}
{v.iRefresh}
{a.hasStr &&
{a.strLabel}
}
))}
} {v.edShowAvatar &&
Codes 2FA (TOTP){v.totpCount}
{v.iPlus} Ajouter
{v.totpRows.map((t, i) => (
{v.iTrash}
))}
}
Notes
Annuler
{v.edSubmit}
} {/* ===================== OVERLAY IMPORT ===================== */} {v.importOpen &&
Importer des mots de passe
{v.importStepLabel}
{v.is1 &&
Choisissez le service d'origine. CléPass adaptera automatiquement les correspondances de colonnes.
{/* DEUX colonnes et non trois : les intitulés d'export doivent tenir sur une seule ligne (« chrome://password-manager/passwords → Exporter » réclame 303 px de texte). Sur trois colonnes il aurait fallu un popup de ~1260 px, soit la largeur de l'écran. */}
{v.importSources.map((s) => (
{s.icon}
{s.name}
{s.desc}
))}
} {v.is2 &&
{v.importSelIcon}
{v.importSelName}
{v.importSelDesc}
{v.importErr &&
{v.importErr}
}
} {v.is3 &&
{v.iShieldCheck}{v.importNbEntrees} entrée(s) détectée(s) · les doublons seront ignorés
{v.importFormat}
Correspondance des colonnes
{v.importMap.map((m, i) => (
{m.from} {m.to}
))}
}
{v.importShowBack &&
Retour
}
Annuler
{v.importNextLabel}
}
); }; })();