Un QR de texte, ou un QR dynamique qui encode un lien de suivi, etait lu puis
ecarte en silence : l'exe repondait "aucun QR 2FA trouve", l'APK continuait de
viser sans rien dire. Le texte d'un autre QR ne part toujours pas a la page ;
seul le fait qu'un QR a ete lu est signale.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- boutons choisis (Full, Password, 2FA, Add current window, Search, Open),
style icones (Segoe MDL2 Assets) ou texte, opacite au repos, allumee ou non
- clic droit sur la barrette : Show icons / Show text en plus de l'opacite
- info-bulles sur chaque bouton
- Add et Search reprennent les corps extraits de Ctrl+Shift+A et Ctrl+Shift+Q
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Le poste genere le QR (DelphiZXingQRCode, Apache 2.0) depuis ses prefs :
URL + jeton, jamais le mot de passe de synchro. Le telephone lit le texte
copie par l'appareil photo et remplit les champs ; Save valide.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Les onglets etaient visibles par defaut puis masques a la reponse de
/ping : Register restait affiche quelques millisecondes. Ils naissent
maintenant invisibles (visibility, place reservee) et ne sont reveles
qu'une fois la reponse connue — licence valide, ils apparaissent sans
rien decaler ; licence refusee, Register n'est jamais montre.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
A l'arrivee sur l'ecran de connexion, /ping est interroge : son 404 JSON
exact ne peut venir, avant connexion, que de la 2e couche. Dans ce cas les
onglets disparaissent et seul Login reste. Tout autre echec laisse
Register visible — le masquer a tort empecherait un vrai client de creer
son compte. Le test porte sur le corps JSON, pas sur le seul statut : le
verrou du jeton repond aussi 404, sans corps.
Cosmetique : /register etait deja refuse cote serveur. On cesse seulement
de proposer un formulaire qui n'aboutit jamais.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Avant toute session, le 404 generique ne peut venir que de la 2e couche :
les routes appelees a ce stade existent toutes, et leurs vraies erreurs
portent un autre texte. api() vide son message, toast() ignore un message
vide — qui a rapiece la 1re couche ne voit rien.
Apres connexion rien ne change : pieces jointes et dossiers renvoient un
404 'Not found' legitime qu'il faut montrer. Prix assume : une route
perdue par un build casse echouerait aussi en silence avant connexion.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
LICENCE-TESTS.md : cinq series. B eprouve ce qui doit tomber (chaine
quelconque, licence tronquee, charge utile trafiquee, autre poste, autre
paire de cles), C le risque symetrique et plus couteux — refuser un
client qui a paye (espaces colles, retour a la ligne, redemarrage
machine). D est dediee a la stabilite de l'empreinte, le defaut deja
rencontre. E delimite la portee de l'activation.
Le plan citait cmd://licence/clear sans moyen de l'appeler : un bouton
"Deactivate (debug)" au bas de Settings -> Licence, revele seulement
quand config.txt existe. Il donne au passage son unique appelant a
ClearLicence, qui etait du code mort.
Le document dit aussi ce qu'aucun essai ne prouve : rapiecer l'exe reste
possible, il n'y a pas de revocation, et le brouillage n'est pas du
chiffrement.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Porteur, courriel, date d'emission et Installation ID, lus dans la charge
utile deja verifiee. Rempli une fois au demarrage sur les deux chemins
(deja licencie, activation reussie) : rien n'y change pendant une
session. Section masquee hors navigateur hote.
Les champs sont echappes avant interpolation dans ExecuteJavaScript bien
qu'ils viennent d'une charge utile signee — signee ne veut pas dire
inoffensive.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
C'etait la decision prise, et je l'avais mal lue : "pour le moment 1"
repondait a la question 1, pas "une seule licence".
PM.MachineId melange l'UUID de la carte mere (WMI) et la serie du volume
systeme, puis n'en publie que 16 hexa : l'empreinte se recopie dans un
courriel, l'UUID brut n'a pas a circuler. Un UUID nul ou tout a FF est
rejete — VM et images clonees l'annoncent, et deux postes tomberaient
alors sur la meme empreinte.
Le champ m est compare APRES la signature. m absent = licence flottante,
acceptee : seule la cle privee peut en emettre, et le champ ne peut pas
etre retire sans casser la signature. Cote script -Machine est
obligatoire sauf -Floating explicite.
L'ecran d'activation affiche l'Installation ID : une licence liee ne peut
plus etre emise avant l'installation.
Verifie sur ce poste : liee a lui acceptee, liee a un autre refusee,
flottante acceptee.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Le cyan de .auth-hint dit "voici l'etat des choses" (session expiree,
coffre verrouille). Un refus n'est pas un etat, c'est un echec : il doit
se distinguer a la couleur avant d'etre lu. Variante .is-error posee sur
le refus, retiree a chaque nouvel essai.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
ensureLicence() en tete d'init(), avant tout le reste : un ecran
d'authentification qui s'affiche puis se fait recouvrir donnerait
l'impression que le coffre etait accessible.
L'ecran reprend les classes de l'ecran d'authentification — zero style
nouveau hormis un z-index. Le statut est demande deux fois avant de le
montrer : un demarrage a froid lent manquerait la fenetre de reponse, et
le silence se lirait comme un refus.
Une cle refusee ne dit pas pourquoi, et le journal note le verdict sans
jamais la cle.
ATTENTION : tant que LicencePublicKeyBlob est a zero, l'ecran est
infranchissable. Poser la cle publique avant de deployer.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
localStorage meurt avec l'origine a chaque lancement, donc un autofill
desactive revenait sur ON. Miroite en pref DPAPI comme unlockMode et
rememberedUsername ; la copie DPAPI fait autorite, relue dans enterApp
juste avant la poussee des combos vers Delphi.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Synchroniser est une ACTION, pas un reglage. Elle n'existait qu'au fond
du panneau des reglages, a cote de l'URL et du jeton : on l'y configure
une fois, on la declenche tous les jours.
Sans URL, sans "Enable sync" ou sans mot de passe de sync, l'appui ouvre
les reglages ET DESCEND jusqu'a la section - un bouton qui ne peut
qu'echouer ne vaut pas mieux que pas de bouton, et repondre "Sync not
configured" laisse chercher ou reparer. Le panneau n'a pas d'onglets,
c'est une colonne longue : le battement dit lequel des blocs on est venu
chercher.
L'etat est lu SANS les secrets - hasEncPwd dit que le mot de passe existe
sans le lire, ce que loadSyncConfig documente : les tirer les planterait
dans le renderer pour la vie du processus.
L'entree disparait sans le pont, comme la section : le frontend PHP n'a
pas de sortie reseau.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Renaming the label from "Sync (WebDAV)" to "Sync (WebDAV or Gitea)" hid the
whole section from the Sync & Backup tab: applySettingsTab maps sections to
tabs BY THEIR VISIBLE LABEL TEXT, so the miss fell through to 'general'. The
section was not lost - it moved to the General tab, which is worse than
missing, because it is nowhere you would look for it.
The map is updated, and the coupling is now written down where the map lives:
these keys are the displayed strings, word for word, and rewording one means
touching both sides.
Checked every key against the labels in index.html; the only sections without
an entry are "Getting started" and "Startup & tray", which belong in General
anyway.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The autofill hotkey already picked whichever field still needed filling; quick search forced the password, so a first unlock or one without a remembered username landed the cursor in the second field.
Both locked-vault paths cancelled the autofill with the default refocus, which returns the foreground to the captured target - right after Delphi had raised our window. Ctrl+Shift+Q flashed for a frame then Notepad took over; Ctrl+Shift+L moved the window twice because focusApp had to take it back.
Assigning .value fires nothing, and the code called soDirtyCheck by hand - so the strength bar, which listens to input, stayed frozen on the previous password. Dispatching the event covers every listener on the field instead of one named handler.
inert removes elements from focus and hit testing, but shortcuts are listened for on the document and never traverse an element. Ctrl+A still selected every entry under the overlay, Ctrl+K would open the palette behind it, j/k moved the cursor - and Delete ran a batch delete while a sync was writing to the same rows.
A renderer dump taken after a quick-unlock, with nothing opened, contained the auto-backup password in cleartext - inside an ExecuteJavaScript string and in the DevTools protocol buffers - and it survived the auto-lock that wipes the vault key.
New prefs/has answers set-or-not without moving the value. The settings panel uses that; only the paths that encrypt a backup, test the WebDAV connection or run a sync fetch the secrets themselves.
renderGrid replaces every card, so the scroll container jumped back to the top: ticking a card near the bottom of a long list threw the user back to the first row. Only the affected card and the selection bar are updated now.
content-header and pagination were exempt from click-away in full, so their large empty areas were dead: clicking beside the title or next to the page buttons left the editor open. Only the actual controls are exempt now.
They accepted any drag then looked for ids in text/plain: dragging text selected in a cell produced NaN, filtered out, so the target invited a drop that could do nothing. Entry drags now carry application/x-pm-entries, mirroring the folder MIME, and all four targets check it before accepting.
Checked ids belong to the view they were made in. Carried into the trash they were LIVE entries while the trash bar offers Delete forever, so a purge could destroy rows that had never been trashed. The checkbox-less views were the visible symptom of the same problem; one rule replaces the special case.
The batch bar survived into vault health, the authenticator grid and the audit log, none of which render checkboxes - it kept offering Delete, Move to folder and Add tag on entries the user could no longer see selected.
Free-floating it either covered the cards or the sidebar navigation. Inside .toast-container it shares the corner with the toasts, and column-reverse keeps it above them.
The pill was pinned to the left edge and covered the navigation, hiding Trash. It also kept counting after Breach check disabled, the workers carrying on with one network round-trip per entry.
Bottom-left, out of the way of the toasts, with a live count. Deliberately not the busy overlay: the scan is network-bound and can take a while, and there is no reason to lock the vault while it runs.
The dashboard reads its pwned column from state.hibpResults but its cache was computed before the scan finished, so enabling the breach check from the health view showed 0 pwned while the entry cards already carried the flag. Disabling had the mirror problem: results cleared, dashboard still counting them.
inert removes the controls from focus but not the document scroll: Space, PageUp/Down, Home/End and the arrows scroll without any focused element, so the page still moved under the overlay.
The overlay covers the page, which stops the mouse but not Tab and Space. The inert attribute removes the subtree from focus, hit-testing and the accessibility tree in one go - applied to every body-level sibling except the overlay, and always cleared in hideBusy.
The busy guard cancelled through the path that hands the foreground back to the captured target. During an export that pushed the native Save dialog behind the other application, leaving the app disabled by a modal the user could no longer find. refocus=0 separates refusing a request from ending a user interaction.
Global hotkeys come from Win32, outside the window, so the busy overlay intercepts nothing: Ctrl+Shift+A opened the editor behind it, unreachable. Guarded in openSlideOver and openQuickSearchModal rather than at each trigger - every caller routes through them, and no save path raises the overlay.
There are two Escape handlers: a capture-phase one for the editor and the global one-surface-per-keystroke chain. Only the first was guarded, so export - which reaches the editor through the global chain - still popped the discard-confirm under the overlay.
The discard-confirm opened underneath the busy overlay, which sits at z-index 200 - blurred and half-clickable. Worse than the display: it let the user arbitrate closing the editor while a sync or a rotation was writing to the same entry.
A 404 on save meant the work had to be copied out by hand. The POST mints a fresh uuid, so this is a new entry rather than a resurrection and cannot fight the tombstone the delete left behind. Gated on status 404 AND the server message: the HTTP layer answers 404 to any request it refuses, which the status alone would misread.
Same placement as the TOTP field, which buys back a column on a dense row: label | value (eye inside) | copy | menu. Pinned top-right on a multiline field so it stays clear of the resize grip.
The inline copy button stays: copy is the frequent action and burying it costs a click every time. Unlike the old lock-versus-eye pair, two ways to copy are not confusable.
The lock button sat next to the eye and both masked the value, so they read as the same control. The eye now carries the state - cyan ring when the field is stored as secret - and flipping the flag moved into a row menu alongside Remove field, one deliberate step away from the everyday reveal. The row drops from six columns to five.
The menu reuses the card kebab classes, so closing it on an outside click is already handled.