Commit Graph

591 Commits

Author SHA1 Message Date
r-zakarya fa2160104c fix(android): la release place elle-meme les ressources generees
Le Build depose a plat dans Android64\<Config>\ les ressources d'Android
- styles.xml, strings.xml, les variantes -v21/-v31, l'icone adaptative -
et c'est le Deploy qui devrait les ranger dans res\. Il le fait en DEBUG
et pas en RELEASE, alors que les proprietes Android_* du .dproj sont
communes aux deux configurations et qu'aucune entree de deploiement ne
les nomme.

Sans elles, aapt refuse le paquet sur "@style/AppTheme" introuvable : le
manifeste reference un theme que plus rien ne definit.

Le script les range donc lui-meme, avant nos propres ressources pour que
notre colors.xml garde le dernier mot. La correspondance nom-fichier vers
sous-dossier est transcrite explicitement : un fichier nouveau se
signalera au lieu d'etre range au hasard.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-29 00:54:24 +01:00
r-zakarya 719522df92 fix(android): aapt refusait le paquet a cause de cinq doublons
Le deploiement de Delphi depose a la racine de res\ une copie a plat de
cinq de ses ressources - ic_launcher_*, splash_* - alors que les vraies
sont dans res\drawable\. Or aapt n'accepte que des DOSSIERS a cette
racine : il refusait le paquet entier, "invalid resource directory name",
une fois par fichier.

On les retire avant l'appel, et rien n'est perdu : ce sont des doublons,
verifies presents dans drawable.

Ne se voyait pas en debogage, ou l'arbre traine depuis longtemps. C'est
la premiere release qui l'a montre, comme le dossier de mise en scene
absent juste avant.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-29 00:41:05 +01:00
r-zakarya 8d51aa54f7 fix(android): la premiere release echouait sur un dossier absent
L'arbre de mise en scene est creuse par l'etape Deploy, qui vient APRES
les trois copies qui s'y deversent. En debogage il existait deja, laisse
par un deploiement anterieur, et personne ne s'en apercevait ; pour une
configuration jamais empaquetee, la premiere copie tombait sur un chemin
absent - "Could not find a part of the path".

Les creer d'abord ne coute rien quand elles existent. C'est exactement le
defaut que TESTS.md annoncait : le chemin -Release n'avait jamais tourne.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-29 00:34:39 +01:00
r-zakarya cbcdb3f593 fix(android): le temoin bleu sait enfin s'eteindre
FRemoteAhead etait remis a faux a trois endroits - publication,
publication differee, rafraichissement - et aucun ne le disait a la page.
Seul l'allumage etait pousse. Le point bleu restait donc affiche apres la
synchronisation qui venait de le rendre caduc, jusqu'au retour suivant
sur l'accueil.

Un temoin qui reste allume apres qu'on a fait ce qu'il demandait ne
demande plus rien : il decore, et on apprend a ne plus le regarder.

Le drapeau et son affichage passent maintenant par SetRemoteAhead, dans
les deux sens. Meme asymetrie, meme correction que pour le point orange.

PROBE_EVERY_TICKS revient a 40 : la bascule a 8 n'etait que pour rendre
la serie E observable en deux minutes.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-29 00:27:40 +01:00
r-zakarya b2c677997e fix(android): un faux echec de synchronisation, et des boutons colles
Le chien de garde mesurait la duree TOTALE, alors que son message dit
"no reply from the server". Sur un coffre devenu gros - 286 Ko - la
reponse arrivait en 841 ms et les vingt secondes suivantes partaient dans
la derivation de la cle puis l'affichage : il criait donc a l'absence de
reponse alors que tout avait reussi, cache ecrit compris. Un faux echec
est pire qu'un vrai, il apprend a se mefier de ce que l'application dit.

Il est desarme des que le statut HTTP est connu, quel qu'il soit : ce qui
suit ne depend plus du reseau, et le voile est leve par le finally.

Au passage, les boutons des reglages n'avaient pas de margin-top : le
champ du code et ses deux boutons se touchaient et se lisaient comme un
seul bloc. Les piles de l'accueil ont leur gap ; ici les elements se
suivent en flux normal, donc c'est au bouton de reserver sa place.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-29 00:22:45 +01:00
r-zakarya d856e51230 docs(android): la serie E demandait une chose impossible
AUTOLOCK_MINUTES = 3 verrouille apres trois minutes d'inactivite, tandis
que PROBE_EVERY_TICKS = 40 - quinze secondes le battement - demande dix
minutes de coffre OUVERT. Le compteur n'avance que tant qu'il l'est, et
LockVault sort avant de l'incrementer. "Laisser ouvert dix minutes sans y
toucher" verrouillait donc a la troisieme, et le sondage ne partait
jamais.

Ce n'est pas un defaut : le temoin sert pendant qu'on se sert du coffre,
et qui s'en sert le touche. Mais sans le dire, on cherche un point qui ne
peut pas paraitre. La serie demande maintenant de toucher toutes les deux
minutes, et offre la contre-epreuve courte - PROBE_EVERY_TICKS a 8 le
temps d'un essai.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-29 00:02:48 +01:00
r-zakarya 23dc0a78ac feat(sync): Sync now dans le menu Tools
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>
2026-08-28 23:59:52 +01:00
r-zakarya fba05f1ce5 fix(import): la restauration dupliquait l'historique, comme la sync
Le meme defaut, par une autre porte : restaurer une sauvegarde par-dessus
des entrees existantes passe aussi par encryptImportEntry puis PUT, donc
re-chiffre un mot de passe inchange et fait croire au serveur qu'il a
change. La sync etait la cause de ce qui se voyait, pas la seule.

La regle est extraite en une fonction, keepCiphertextIfSamePassword,
appelee par les deux. Les re-publications partielles - tag, dossier,
migration des metadonnees - sont hors de cause : elles reutilisent deja
le chiffre stocke.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-28 23:47:36 +01:00
r-zakarya 8905599214 fix(sync): la synchronisation dupliquait l'historique des mots de passe
L'instantane transporte le mot de passe EN CLAIR, et encryptImportEntry
le re-chiffre avec un IV neuf : le chiffre differe donc toujours, meme
quand le mot de passe n'a pas bouge. Or le serveur decide d'ecrire une
version dans l'historique en comparant les chiffres
(`encrypted_password <> :newenc`) - il voyait une difference la ou il n'y
en avait aucune, et chaque synchronisation qui redescendait une entree y
deposait un doublon date du jour. L'historique montrait la meme valeur
deux fois, a des jours d'ecart.

Le serveur ne peut pas trancher, il ne voit que du chiffre. Seul le
client connait les deux clairs : il compare, et rend le chiffre d'origine
quand ils sont egaux - ce que le commentaire du handler appelle deja
"JS reuses originalEncrypted bit-for-bit".

Trouve en deroulant la serie H sur le telephone.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-28 23:33:21 +01:00
r-zakarya 4fda63ec30 feat(android): creer une entree et son dossier d'un seul geste
"+ new folder" n'existait qu'a l'edition. Creer une entree ET son dossier
demandait donc trois gestes - creer, rouvrir, changer le dossier - pour
ce que le meme controle sait faire ailleurs en un seul. L'asymetrie ne se
voyait qu'en s'en servant.

Deux cas que le chemin de l'edition ne posait pas : un champ laisse vide
et le marqueur interne du "+ new folder" retombent tous deux sur All -
sans quoi valider sans rien taper aurait envoye au coffre un nom de
dossier fait d'un caractere de controle.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-28 22:54:46 +01:00
r-zakarya 363b934e8a fix(android): le temoin d'attente parait des l'accueil, sans ouvrir le coffre
FPending est relu au demarrage, mais PM.setPending n'etait pousse que par
OpenLocalNow : la page n'apprenait donc l'attente qu'en OUVRANT le
coffre. Un demarrage a froid montrait Sync sans point orange - l'ecran ou
l'on decide de synchroniser, et le seul moment ou ce drapeau relu au
demarrage sert a quelque chose. Il fallait ouvrir puis reverrouiller pour
qu'il paraisse.

Les deux temoins se reposent maintenant dans ShowUnlock, donc a chaque
retour sur l'accueil.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-28 22:31:27 +01:00
r-zakarya 99af31a111 docs(android): l'ordre des series n'est pas alphabetique
La serie A guette un point orange sur un coffre ne ici, ou il ne peut pas
paraitre : CommitChange voit LLocalOnly, ecrit localement et ne publie
rien - il n'y a pas de depot tant qu'on n'a pas reconcilie. On aurait
conclu a un defaut la ou il n'y a que l'ordre.

C reconcilie, donc C passe avant A.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-28 22:05:29 +01:00
r-zakarya 277e710754 docs(android): TESTS.md dit d'ou l'on part
Plusieurs series agissent sur une entree existante, sans dire d'ou elle
vient. Partir d'un coffre vide les rend ininterpretables : on ne
distingue pas "la modification n'a pas voyage" de "il n'y avait rien a
faire voyager".

Etat de depart : coffre neuf, une seule entree, un seul dossier - All, et
c'est le premier enregistrement qui le fait apparaitre.

Le coffre vide reste un cas a eprouver, mais nomme comme tel : c'est lui
qui a revele que "coffre ouvert" se lisait sur le nombre d'entrees.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-28 21:55:05 +01:00
r-zakarya eb49a4ab50 docs(android): TESTS.md decrit l'application telle qu'elle est
La version precedente decrivait une application qui n'existe plus - deux
secrets confondus, des reglages qui n'appliquaient rien sur-le-champ, un
coffre qui s'ouvrait sans rien demander.

Trois series nouvelles : les deux secrets (C), l'ouverture qui demande
quelque chose (D), et le clavier avec le retour d'application (I). F est
reprise en entier. La remise a zero passe desormais par le bouton Debug
plutot que par adb, ce qui rend le scenario deroulable sans machine.

En tete, ce que la seance a vraiment appris : les defauts se tiennent par
la main, et le journal doit se lire AVANT de retenter - la
synchronisation le vide.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-28 21:52:43 +01:00
r-zakarya 85691bb2f1 feat(android): un bouton pour jeter le coffre local, en debogage seulement
Il vit en bas des reglages, apres tout le reste, et demande deux appuis :
un effacement sans retour ne se declenche pas sur un doigt qui glisse.

Absent en release, et pas seulement cache : la commande du pont n'est pas
compilee et Delphi ne devoile la section que dans une version de
debogage. Le bouton ne peut donc pas reapparaitre par accident. Verifie -
Release compte 8060 lignes contre 8072 en debug, soit exactement ce bloc.

DropLocalVault efface le cache, le conteneur local et le drapeau
d'attente, chacun sous son propre try : un fichier absent ou verrouille
ne doit pas empecher les deux autres de partir. Les reglages du depot
restent - c'est le coffre qu'on jette, pas la facon d'aller le rechercher.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-28 21:47:53 +01:00
r-zakarya d43176576f style(android): les boutons du bandeau sont ronds 2026-08-28 17:52:18 +01:00
r-zakarya cc51540283 fix(android): le clavier se replie une fois l'appui passe
Retenir le focus au mousedown fait atteindre la cible du premier coup,
mais laissait le clavier ouvert : apres "Open vault" il restait dresse
par-dessus la liste. Les deux moities vont ensemble - on retient le focus
le temps de l'appui, on le lache des que l'action est partie.

Sauf si le bouton agit sur le champ lui-meme : un oeil qui devoile, une
fleche qui deroule. Ceux-la vivent dans la meme rangee que le champ, et
replier le clavier apres eux obligerait a le rouvrir pour continuer.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-28 17:47:59 +01:00
r-zakarya 0ec32a7c19 fix(android): le message ne se cache plus derriere le clavier
Le toast vit a bottom: 14px, donc sous le clavier des que celui-ci est
leve : il ne se decouvrait qu'en quittant l'ecran, quand le clavier se
repliait. Et depuis qu'un appui sur un bouton ne vole plus le focus, le
clavier RESTE ouvert - ce qui a rendu systematique un defaut jusque-la
occasionnel.

Il remonte au-dessus de la reserve que body.typing menage deja pour le
clavier : meme mesure, meme raison.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-28 17:43:26 +01:00
r-zakarya 0930a866c2 fix(android): le premier appui n'est plus mange par le clavier
Un bouton touche pendant qu'un champ a le focus recevait d'abord le
blur : le clavier se repliait, la reserve de body.typing disparaissait,
la page remontait - et le bouton n'etait plus sous le doigt quand le clic
partait. Il fallait appuyer deux fois, la premiere ne servant qu'a fermer
le clavier.

Empecher le defaut du mousedown empeche le TRANSFERT DE FOCUS, pas le
clic : le champ garde le curseur, rien ne bouge, et l'appui atteint sa
cible du premier coup. Pose sur toute la page - le piege n'a aucune
raison d'etre propre aux reglages.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-28 17:40:19 +01:00
r-zakarya b3f725137e fix(android): le message se voit dans les reglages, et le code s'y cache coffre ferme
Le toast n'avait pas de z-index : il se peignait DERRIERE l'ecran des
reglages, qui est a 80. "Unlock code set" partait bien, mais sous la
page, et ne se decouvrait qu'en sortant - quand il ne s'etait pas deja
eteint. Un message qui ne se voit pas la ou on l'a provoque ne sert a
rien.

La section du code de deverrouillage ne parait plus que coffre ouvert.
Poser comme retirer exigent le clair en main : coffre ferme, les deux ne
pouvaient que refuser, et proposer une action qui refuse toujours vaut
moins que ne rien proposer.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-28 17:36:37 +01:00
r-zakarya d428a6fc7f feat(android): un coffre telecharge n'est plus lisible sans rien demander
Sans biometrie utilisable et sans code pose, le cache s'ouvrait sans rien
demander - or il CONTIENT le mot de passe de sync. Qui tenait le
telephone deverrouille obtenait donc le coffre ET le secret du depot.

Il est desormais scelle sous le mot de passe qui vient de le rapporter :
celui-ci est deja dans ce fichier, donc rien de neuf n'est expose, et
l'utilisateur le connait puisqu'il vient de le taper. C'est aussi la
seule forme de cache qui survive a une reinitialisation du verrouillage
d'ecran.

Pas quand l'appareil sait authentifier : VER_PIN et VER_WRAP s'excluent,
et imposer une frappe a qui a une empreinte serait un recul.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-28 17:25:16 +01:00
r-zakarya 5cc80727d6 fix(android): l'exception etait lue apres sa liberation
Register et Login mettaient leur gestionnaire d'erreur en file avec
TThread.Queue, puis y lisaient E.ClassName et E.Message. Or la file
s'execute sur le thread principal bien apres que le except a rendu la
main, et Delphi libere E en sortant du gestionnaire : c'etait une lecture
apres liberation.

Elle rendait un message vide - "Login failed: EPMACrypto - " sans la
raison - et parfois une violation d'acces qui remontait a FMX sans rien
laisser au journal. Le vrai motif de l'echec n'a donc jamais ete affiche.

On ne met plus en file que des chaines, qui survivent.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-28 16:46:24 +01:00
r-zakarya 4e0223bee5 fix(android): aucune exception ne sort du pont sans laisser son nom
Le dispatch n'etait garde par rien : une faute dans un handler remontait
jusqu'a FMX, qui affichait "Access violation" avec une adresse, tandis
que le journal ne montrait que la commande recue suivie de rien. On
savait qu'une commande echouait, jamais laquelle ni pourquoi.

On attrape, on nomme, et on rend la main - une commande fautive ne doit
pas laisser l'interface voilee derriere une boite de dialogue.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-28 16:39:10 +01:00
r-zakarya a41c61405c fix(android): retirer le code de deverrouillage demande de le connaitre
Le coffre ouvert ne prouvait rien : il peut l'etre depuis une heure et
rester a l'ecran. Quiconque trouvait le telephone dans cet etat levait la
protection en deux appuis, sans que rien ne le lui demande.

Le code courant voyage maintenant avec la demande, et Delphi le compare a
FPinSealed - ce sous quoi le cache est REELLEMENT scelle, pose seulement
par une ouverture reussie, jamais par un champ.

Fermee au passage : SetUnlockCode('') retirait le code en silence, ce qui
contournait le controle qu'on vient de poser.

Le FBusy de ClearUnlockCode ne sort plus sans un mot - une sortie muette
se lit comme une reussite.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-28 16:30:30 +01:00
r-zakarya 203f45eb7c fix(android): les refus se voient, et la croix se recale sur le champ
Un refus s'affichait du meme gris qu'un etat : "Master password: 8
characters at least" se lisait comme "Vault on this device", et rien ne
disait que l'application venait de refuser. StatusErr le met en rouge -
deux procedures plutot qu'un drapeau devine du texte, parce que trier des
messages sur leur contenu a deja detruit un cache dans ce projet. C'est
l'appelant qui sait s'il refuse.

La croix des champs etait calee a top: 8px, en compensation du margin-top
du champ. Les piles annulent ce margin depuis qu'on y a unifie
l'espacement, et la croix restait donc 8 px trop bas. Elle s'ancre
desormais au bord INFERIEUR, le seul que le champ touche dans les deux
cas.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-28 16:25:07 +01:00
r-zakarya 494efddb44 feat(android): le mot de passe de creation ouvre le coffre
Il ne servait qu'une fois, a la creation : le cache s'ouvrait ensuite
sans rien demander, et n'importe qui tenant le telephone deverrouille
lisait le coffre.

Il est desormais pose comme code de deverrouillage - c'est exactement ce
que ce format sait faire, et il accepte un mot de passe entier aussi bien
que des chiffres depuis qu'on a retire le clavier numerique. Le cache
porte donc VER_PIN, la seule forme qui survive aussi a une
reinitialisation du verrouillage d'ecran.

Retirable en deux appuis depuis les reglages, pour qui prefere
l'ouverture immediate.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-28 16:14:54 +01:00
r-zakarya 9b26a1dbd0 fix(android): un coffre vide n'est pas un coffre ferme
"Coffre ouvert" se lisait sur le nombre d'entrees. Or un coffre vide en
compte zero, exactement comme un coffre ferme - et le coffre d'essai
l'est. Le garde de DoSync restait donc vrai apres l'ouverture, rearmait
la reprise, et l'on tournait en rond : le journal repetait "Vault opened
from cache", l'interface clignotait et ne repondait plus.

Le meme idiome se trouvait a deux autres endroits. Le second compte :
OnIdleTimer se coupait de lui-meme sur un coffre vide, qui restait donc
ouvert indefiniment - et vide aujourd'hui ne veut pas dire vide dans dix
minutes.

FPlain, le clair en main, est le seul signal qui ne mente pas.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-28 16:12:20 +01:00
r-zakarya af28788a0a fix(android): Sync ouvre le coffre des qu'il y en a un, pas seulement sous code
Le garde ne couvrait que le cache scelle par un code. Depuis que les deux
secrets sont separes, la reconciliation a besoin de celui du conteneur
local - qui ne se pose qu'a l'ouverture - donc un coffre ne ici refusait
aussi, avec "open vault first", et ouvrir ensuite ne relancait rien : le
bouton Sync ne vit que sur l'accueil, et le coffre ouvert on est sur la
liste.

Un seul garde desormais : s'il y a un cache lisible et que le coffre
n'est pas ouvert, on ouvre et on reprend la synchronisation. Un appareil
sans cache n'est pas concerne - c'est le premier telechargement.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-28 16:07:25 +01:00
r-zakarya dd44a164f6 fix(android): le cache retient le secret qui ouvre CE coffre
La separation laissait FVaultPwd vide apres une ouverture depuis le
cache - seules la creation et la connexion le posaient - et la
reconciliation refusait donc en boucle, "open vault first" alors que le
coffre etait ouvert.

Le cache porte un secret et un seul : celui du conteneur local pour un
coffre ne ici, celui du depot pour un coffre publie. Les deux ne
coexistent jamais, puisque la reconciliation detruit le conteneur local
en passant l'autorite au depot. CachePwd choisit a l'ecriture, et la
relecture le range dans la meme case.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-28 16:04:04 +01:00
r-zakarya ed7ccdd2d6 feat(android): deux secrets separes, comme sur le poste
Le mot de passe de creation scellait le conteneur local ET pretendait
sceller l'instantane du depot. Un coffre cree ici publiait donc sous son
propre mot de passe, que le poste ne savait plus ouvrir - et le
rafraichissement echouait avec "wrong password" sur un depot pourtant
intact.

FVaultPwd ouvre le conteneur local et s'arrete la. Le secret du depot vit
dans les reglages, doit etre le meme sur tous les appareils, et c'est lui
seul qui ouvre et rescelle l'instantane.

Les deux ne se rencontrent qu'a UN endroit, la reconciliation : le
conteneur local s'ouvre sous le sien, puis la cle est jetee avant de
toucher au depot - la garder rescellerait le depot sous le mauvais
secret. Ailleurs ils ne se croisent jamais : un coffre ne ici ne publie
pas, un coffre publie n'a plus de conteneur local.

Migration : rien a rechiffrer. Les coffres existants ont simplement les
deux valeurs egales, et cessent d'etre forces de le rester.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-28 15:58:17 +01:00
r-zakarya 17cfc11084 fix(android): le mot de passe des reglages suit le coffre a CHAQUE ouverture
La correction precedente ne portait que sur la creation et la connexion
locales, qui n'arrivent qu'une fois. Le chemin reellement emprunte est
l'ouverture depuis le cache, a chaque lancement : PushConfig y reposait
la valeur de premier lancement, l'ouverture ne la remplacait pas, et
DoSync la prefere a celle du coffre. Les reglages continuaient donc
d'afficher TEST_PWD.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-28 15:44:54 +01:00
r-zakarya ea2450abfd feat(android): dire pourquoi le cache est illisible, et accepter un mot de passe comme code
"Local vault unreadable" laissait croire a une corruption, donc a une
perte, alors que le coffre est intact dans le depot. Quand Android a
lui-meme detruit la cle - verrouillage d'ecran retire ou reinitialise -
l'application le dit maintenant, et dit aussi que rien n'est perdu.

Le tri passe par un drapeau pose la ou l'invalidation se constate, pas
par un test sur le texte du message : c'est ce tri-la, fait sur des
chaines, qui detruisait autrefois le cache en se trompant.

Le code de deverrouillage accepte un mot de passe, et l'a toujours
accepte - aucune validation n'a jamais impose des chiffres. Seul
inputmode="numeric" le laissait croire, et le clavier numerique
decourageait d'essayer. Clavier complet, libelles et note mis a jour.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-28 15:24:29 +01:00
r-zakarya 8b4130f13e fix(android): les reglages affichaient un mot de passe qui n'etait pas celui du coffre
PushConfig pose FPwd a la valeur de premier lancement - en debogage une
constante, en release du vide - et rien ne la remplacait ensuite. Creer
ou ouvrir un coffre local mettait a jour FSyncPwd seul, si bien que les
reglages continuaient d'afficher l'autre.

Ce n'etait pas qu'un affichage : DoSync PREFERE FPwd a FSyncPwd, au nom
de "ce qui est tape l'emporte" - or rien n'avait ete tape. Les deux
chemins de synchronisation, le rafraichissement de fond et le bouton,
travaillaient donc avec deux mots de passe differents sur le meme coffre.

Le coffre qui vient de s'ouvrir dit desormais son mot de passe, et les
reglages le montrent.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-28 15:06:27 +01:00
r-zakarya a81fd88414 fix(android): la page revenait pas apres un verrouillage hors du coffre
Le masquage ne demande rien - il suffit de ne pas etre sur l'accueil pour
que la vue disparaisse avant la capture des applications recentes, coffre
ouvert ou non. La restitution, elle, exigeait un coffre ouvert.

Consulter les REGLAGES coffre ferme, puis laisser le telephone se
verrouiller, cachait donc la page pour de bon : au retour il ne restait
que le fond de la fenetre, un aplat bleu sans rien dessus et sans rien
pour en sortir.

La vue revient maintenant dans tous les cas, et APRES le verrouillage
eventuel pour ne jamais montrer une liste qu'on vient d'effacer.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-28 14:58:18 +01:00
r-zakarya 1a19c838cd fix(android): un seul espacement entre les elements d'une pile
Un champ porte son propre margin-top de 8 px, dont il a besoin dans les
reglages ou les champs se suivent en flux normal, sans gap. Dans une
pile, ce margin s'ajoutait au gap : deux champs se retrouvaient a 16 px
l'un de l'autre, un champ et un bouton a 8. Dans une pile, c'est le gap
qui commande.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-28 14:51:45 +01:00
r-zakarya ab5eeb60f5 fix(android): l'accueil se lit de haut en bas, et ses blocs respirent
L'espace de 8 px vit A L'INTERIEUR de chaque pile. "Create vault" et
"Download vault" etaient dans deux piles voisines et se touchaient donc,
tandis que "Download vault" et "Settings", dans la meme pile,
respiraient. Un margin entre piles adjacentes suffit.

L'ordre suit maintenant les frequences et non l'ordre du code : aller
chercher le coffre, regler, creer en dernier. La creation etait en tete
parce que son bloc etait ecrit en premier dans la page.

L'accent suit le geste attendu - il va a "Download vault" des qu'un depot
est configure, et ne reste sur la creation que lorsqu'il n'y a rien a
aller chercher.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-28 14:42:54 +01:00
r-zakarya 900f5ba740 fix(android): poser, changer et retirer le code exigent un coffre ouvert
Deux branches de l'ouverture posaient le code a partir de ce qui trainait
dans les champs.

La premiere appliquait un "nouveau code" depose par les reglages. Elle
n'exigeait rien de celui qui le deposait : n'importe qui tenant le
telephone deverrouille pouvait en poser un, et c'est le proprietaire qui,
en ouvrant son coffre, l'appliquait contre lui-meme.

La seconde rescellait sous le code tape des que le cache ne l'etait plus,
ce qui DEFAISAIT LE RETRAIT : on retirait le code, le champ le portait
encore, et l'ouverture suivante le reposait sans qu'un mot le dise. Le
premier octet du cache le disait - VER_PIN apres un retrait annonce
reussi.

Poser et changer passent maintenant par SetUnlockCode, jumeau de
ClearUnlockCode : geste explicite, coffre deja ouvert - donc code courant
connu - et effet immediat. Le champ de l'accueil ne veut plus dire qu'une
chose, "ouvre avec ceci", et ne parait que lorsqu'un code est pose.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-28 14:17:24 +01:00
r-zakarya 45564e7266 fix(android): un seul bouton sur l'accueil, et il ne ment pas sur sa destination
Il y en avait deux, "Settings" et "Log", et le second a fini par ouvrir
le premier depuis que le journal a demenage dans les reglages. Un bouton
nomme "Log" qui ouvre "Settings" se ment sur son etiquette.

Settings redevient VISIBLE EN PERMANENCE. Le cacher des qu'un coffre
existe laissait l'accueil sans aucune porte vers les reglages - et c'est
precisement quand le coffre refuse de s'ouvrir, code faux ou adresse
fausse, qu'on en a besoin avec le journal. Ce n'est pas un doublon de la
roue dentee : celle-ci est DANS le coffre, ce bouton est sur l'accueil,
jamais cote a cote.

En release, c'est desormais le bouton des reglages qui s'efface, l'appui
long sur la ligne d'etat le ramenant comme avant.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-28 14:09:17 +01:00
r-zakarya fe00fe8484 fix(android): le journal se deplie dans les reglages, la ou on le demande
Il n'affichait rien, et pas parce qu'il etait vide. togglePanel
parcourait ['cfg','log'] et cherchait pour chacun le bouton qui le
commande - or btnCfg a perdu son data-panel le jour ou les reglages sont
devenus un ecran. La premiere iteration levait sur un null, et le journal
n'etait jamais deplie, quel que soit le chemin.

Il vivait de plus sur l'accueil : "Show the log" refermait les reglages
pour le deplier derriere, c'est-a-dire sur un ecran qu'on ne regarde plus
des que le coffre est ouvert. Il est maintenant DANS les reglages, deplie
sur place - le seul ecran atteignable depuis l'accueil comme depuis le
coffre.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-28 12:55:06 +01:00
r-zakarya 6c5fb2f4ea fix(android): Sync ouvre puis synchronise, et l'accueil ne demande plus le nouveau code
Le garde pose juste avant fermait la seule porte : le bouton Sync ne vit
que sur l'accueil, et une fois le coffre ouvert on est sur la liste, ou
il n'existe pas. Synchroniser devenait impossible des qu'un code etait
pose. Sync ouvre donc le coffre - ce qui verifie le code, seul LoadVault
le peut - et OpenLocalNow reprend la synchronisation ou elle s'est
arretee. Le drapeau est consomme des sa lecture, pour qu'un code faux ne
laisse pas une synchronisation en embuscade.

Le champ "new unlock code" quitte l'accueil : les reglages posent et
changent le code, il y faisait doublon sur l'ecran ou l'on veut seulement
ouvrir. Il reste dans la page, cache - c'est lui que "Set this code"
remplit avant pushConfig.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-28 12:50:15 +01:00
r-zakarya 138abea88d fix(android): Sync ouvrait le coffre sans demander le code
Le garde de DoSync ne couvrait que l'empreinte, et les deux protections
s'excluent : un cache scelle par un code porte VER_PIN, donc
VaultNeedsAuth y est FAUX. Synchroniser reconstruisait le coffre depuis
le depot et l'affichait sans que le code soit demande - il ne gardait
qu'un bouton sur deux.

Exiger un champ rempli n'aurait rien valu : le depot est scelle par le
mot de passe de sync, pas par le code, donc rien ici ne le confronte a
quoi que ce soit et n'importe quelle frappe serait passee. Seul LoadVault
sait si le code est le bon, et c'est "Open vault" qui l'appelle - d'ou le
renvoi vers lui.

Place AVANT ReconcileWithRemote et PublishPending, qui sortent de DoSync
par leurs propres chemins.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-28 12:41:36 +01:00
r-zakarya ac3e9db4d9 feat(android): l'accueil ne repete plus Settings quand le coffre est la
La roue dentee de la liste mene au meme ecran : le repeter sur l'accueil
n'encombrait que l'accueil.

Mais pas de suppression pure et simple. Sans coffre local, la liste
n'existe pas - donc la roue dentee non plus - et il faut bien pouvoir
saisir l'adresse du depot pour aller le chercher. Le bouton parait donc
exactement quand "Open vault" ne parait pas, sur le meme signal.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-28 12:36:49 +01:00
r-zakarya 35d0d8ddf3 feat(autofill): la barrette revient allumee si elle l'etait
Pref DPAPI lazyOn, reprise par RestoreLazyBar depuis FormCreate - apres
le theme, parce que la barrette se peint elle-meme, et apres le cablage
de OnTrayRestore, dont depend son bouton Open.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-27 14:26:18 +01:00
r-zakarya 0604cca148 feat(autofill): un bouton Open, et le menu ne passe plus sous la barrette
Quand la barrette est allumee, elle est souvent la seule chose de
l'application a l'ecran : revenir au coffre ne doit pas demander de
retrouver l'icone du tray.

Le menu d'opacite passait derriere : le battement qui repose la barrette
en tete de la bande TOPMOST cinq fois par seconde recouvrait le menu que
la barrette venait d'ouvrir - lui aussi dans cette bande. TrackPopupMenu
est modal, donc le minuteur battait pendant tout ce temps. Il s'arrete
maintenant le temps du menu.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-26 17:35:36 +01:00
r-zakarya 6b908e73a5 fix(autofill): la barrette reste au-dessus, et se traine depuis ses boutons
TOPMOST n'est pas un rang, c'est une bande. La barre des taches y est
aussi, et cliquer son fond vide la remonte en tete de cette bande : une
barrette posee dessus passe DERRIERE et semble avoir disparu. Le meme
battement de 200 ms la repose.

Traine depuis un bouton : les boutons sont subclasses, et DragDetect
tranche entre clic et glissement en respectant le seuil regle par
l'utilisateur. Sur un clic, on envoie BN_CLICKED a la place du bouton -
DragDetect a avale le WM_LBUTTONUP, lui rendre la main le laisserait
enfonce pour toujours.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-26 17:27:38 +01:00
r-zakarya 9ce07ae5d0 feat(autofill): la barrette revient la ou on l'avait posee
Pref DPAPI lazyPos, ecrite sur WM_EXITSIZEMOVE - une ecriture par
deplacement, pas une par pixel parcouru.

La position n'est reprise que si MonitorFromRect la trouve encore sur un
ecran. La barrette n'a pas d'entree dans la barre des taches : une
position heritee d'un ecran debranche, ou d'une resolution changee, la
laisserait hors du bureau et definitivement irrecuperable.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-26 16:20:02 +01:00
r-zakarya 7fa26858e3 feat(autofill): la barrette s'efface quand la souris la quitte
Clic droit dessus -> 100/75/50/25 %, persiste en pref DPAPI. Pas dans
settings_json : la barrette peut etre allumee coffre ferme, ou aucune
session n'existe pour lire les reglages du serveur.

Le survol est sonde toutes les 200 ms plutot que suivi par
TrackMouseEvent : avec quatre fenetres enfants, passer de la barrette a
un bouton envoie deja un WM_MOUSELEAVE au parent, et il faudrait suivre
la souris sur les cinq fenetres pour recoller les morceaux.

Le menu appartient a la fenetre message-only : TrackPopupMenu reclame un
SetForegroundWindow pour se refermer seul, et le faire sur la barrette
detruirait le NOACTIVATE qui la fait fonctionner.

Transparente n'est pas traversante - les clics sont toujours avales.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-26 16:14:15 +01:00
r-zakarya f7665b4225 feat(autofill): fermer la barrette depuis la barrette, et des libelles lisibles
Un quatrieme bouton X. Il POSTE WM_LAZY_CLOSE a la fenetre message-only
plutot que de detruire sur place : liberer une fenetre depuis son propre
gestionnaire libererait, en plein envoi, l'instance qui lui sert de
window proc.

Au passage, deux libelles sortaient en mojibake - "Quick search__|" dans
le menu du tray, un tiret cadratin dans l'infobulle. Le fichier est en
UTF-8 SANS BOM, que Delphi lit en ANSI, un octet par caractere : les
litteraux restent donc en ASCII.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-26 15:52:50 +01:00
r-zakarya 52f5024de0 feat(autofill): un troisieme bouton 2FA, et la barrette suit le theme
SetPreferredAppMode(ForceDark) ne themise que ce que le systeme dessine
lui-meme - menus, ascenseurs, infobulles. Un bouton Win32 standard reste
donc gris clair sur fond sombre, ce que la capture montrait.

BS_OWNERDRAW + WM_DRAWITEM : le systeme garde le suivi de la souris et
l'etat enfonce, nous ne reprenons que les couleurs, recopiees des
variables de css/style.css. Le cadre passe dans WM_ERASEBKGND plutot que
par WS_BORDER, qui se sert dans les couleurs systeme.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-26 15:49:49 +01:00
r-zakarya 09bbd3fe9e feat(autofill): lazy mode - une barrette de deux boutons pour remplir a la souris
Menu du tray -> "Lazy mode". Deux boutons Full / Password qui flottent
au-dessus de tout et font ce que font Ctrl+Shift+L / Ctrl+Shift+P.

WS_EX_NOACTIVATE est toute l'astuce : une fenetre ordinaire prendrait le
premier plan au clic, le champ que l'utilisateur vient de cliquer perdrait
son curseur, et GetForegroundWindow nous designerait nous-memes comme cible.
Le clic ne deplace donc jamais le premier plan.

Le corps du handler WM_HOTKEY est extrait dans FireAutofillRequest : hotkey
et barrette partagent la capture de la cible, le matching de titre et le
picker. Un seul chemin, deux gestes.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-26 15:42:49 +01:00