Commit Graph

87 Commits

Author SHA1 Message Date
r-zakarya 9999dd113d fix(autofill): AltGr et caracteres sans touche vers VNC
AltGr envoye en Ctrl gauche + Alt droit etendu, avec scancodes : "@" et
"#" en AZERTY arrivaient sans AltGr. Un caractere sans touche dans la
disposition locale est saute et compte dans le journal (copier a la place).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 13:45:33 +01:00
r-zakarya a3e63ee8f1 fix(autofill): vraies touches vers un visualiseur VNC
VK_PACKET (texte Unicode) n'est pas retransmis par TigerVNC : le Mac
distant recevait un V par caractere. Cible VNC -> touches du clavier via
VkKeyScanEx dans la disposition de la cible, modificateurs compris.
Les autres fenetres gardent l'envoi Unicode.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 13:34:47 +01:00
r-zakarya f112de508b feat(notes): commandes sur plusieurs lignes recollees en une seule
Fin de ligne en \ (bash), ` (PowerShell) ou ^ (cmd) : la ligne suivante
est rattachee, et la commande part sur une ligne - un retour a la ligne
tape dans un terminal la lancerait a moitie ecrite. Teste.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 19:08:08 +01:00
r-zakarya 6ca1e4b2cf feat(notes): commandes masquees avec l'oeil des notes
Partage d'ecran : premier mot et 3 derniers caracteres visibles
(ssh ••••.11), dans le panneau et la recherche rapide. Filtre, copie et
remplissage gardent le vrai texte. Aide de la recherche rapide adaptee aux
commandes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 18:35:37 +01:00
r-zakarya bbd4e2ef06 feat(notes): notes de commandes, une commande copiee ou tapee a la fois
Modele "commands" : "# titre" ouvre un groupe, chaque ligne est une
commande. Le corps reste du texte (aucune colonne, sync et APK inchanges).
Panneau : liste avec Copier. Recherche rapide : la note s'ouvre sur sa
liste filtrable ; Entree tape la commande sans Entree et sans vider le
champ, Ctrl+Entree copie, Esc revient. Decoupage teste.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 18:24:49 +01:00
r-zakarya 63e532d5bb feat(entries): Precedent / Suivant parmi les entrees ouvertes
Fleches dans l'en-tete du panneau et Alt+fleches, comme un navigateur :
un clic par erreur dans une liste filtree ne force plus a refaire la
recherche. Historique en memoire (50), vide au verrouillage ; la navigation
repasse par openSlideOver et sa confirmation de modifications non
enregistrees. Logique pure testee (history.test.js).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 17:39:24 +01:00
r-zakarya 28f8a42846 feat(hotkeys): chaque raccourci se desactive (Suppr pendant la capture)
Off = vk 0, non enregistre : la combinaison reste libre pour les autres
applis. Combinaisons retenues en pref DPAPI et rejouees au demarrage, pour
qu'un raccourci coupe le reste coffre ferme. Esc annule enfin la capture au
lieu de fermer les reglages.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 18:15:29 +01:00
r-zakarya 3051c3c392 feat(sync): WebDAV sur l'APK, et le QR du poste qui le porte
Jeton "utilisateur:mot-de-passe" sur une URL ni Gitea ni GitHub = WebDAV,
authentification Basic. Fichier lu et ecrit tel quel ; l'ETag fait le sha
(If-None-Match, If-Match, 412 -> conflit). Clair autorise vers l'IP fixe du
poste seulement. Eprouve contre le WsgiDAV local : 304, PUT 204, puis
synchro du poste. Docs : CLAUDE.md, android-client/README.md (page eveillee
comprise).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 23:13:05 +01:00
r-zakarya 247771bfec docs: formulaire d'edition, scan QR 2FA, synchro et reseau, Copy / Fill
CLAUDE.md : interrupteur Copy / Fill de la recherche rapide et pourquoi le
tray copie seulement ; QR lu mais pas de 2FA ; nom par defaut data.json,
messages de commit neutres, 8 s / 3 essais sur le reseau.
README Android : edition de l'entree entiere en un commit, oeil, icone depuis
la galerie, scan 2FA ; verification a l'ouverture, depot vide = creation ; les
pieges 'view' qui defocalisait les champs et 'toast' qui etait un <div>.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 18:42:12 +01:00
r-zakarya 8d3e20b702 docs(fillbar): info-bulle et detour NOTOPMOST, les deux pieges corriges
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 18:40:38 +01:00
r-zakarya 29d569c7ba docs(fillbar): la barre des taches active passe devant, mesures a l'appui
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-22 15:56:58 +01:00
r-zakarya cfa2a798e5 docs(build): build-both.cmd sauvegarde les exe precedents dans PMServer-previous.zip
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-18 22:58:39 +01:00
r-zakarya 72f9acd733 feat(totp): scanner le QR 2FA affiche a l'ecran (capture + ZXing), seul un otpauth:// part vers la page
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-18 22:24:35 +01:00
r-zakarya 365ee2f596 docs(autofill): note l'idee de la case no-autofill dans l'editeur
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-18 15:31:11 +01:00
r-zakarya 994aaccac4 docs(autofill): etiquette no-autofill
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-18 15:29:37 +01:00
r-zakarya ac97d6c207 docs: reglages de la barrette, GitHub et depot prive, erreurs et mot de passe de synchro sur le telephone
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-18 11:39:32 +01:00
r-zakarya 3eb26e23e5 docs(sync): note l'idee d'un bouton Reinitialiser l'historique, sans la faire
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 22:55:18 +01:00
r-zakarya 034fa48994 docs: QR des reglages, trois facons d'ouvrir, scanner, AllowCreateLocal, debuggable
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 22:19:01 +01:00
r-zakarya d5234e1390 fix(licence): refus de la 2e couche indiscernable d'une route inconnue
Le 404 a corps vide etait remplace par la page HTML d'Indy, distincte du
404 JSON d'une route inexistante : on pouvait lister les routes reelles.
La 2e couche renvoie maintenant exactement le meme JSON.

Ajoute un exe d'essai qui saute l'ecran d'activation, pour eprouver la
2e couche depuis l'application. Define de compilation
LICENCE_TEST_SKIP_SCREEN, jamais un reglage : un interrupteur dans
config.txt ferait sauter la 1re couche sans rapiecage. Build dedie,
.dcu a part ; verifie que seul l'exe d'essai porte le define.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 05:36:28 +01:00
r-zakarya a7af9af47e fix(build): lier SQLite dans l'exe, qui chargeait sqlite3.dll du PATH
Sans FireDAC.Phys.SQLiteWrapper.Stat, FireDAC charge sqlite3.dll au
demarrage. Sur ce poste ca marchait quand meme : le dossier bin
d'Embarcadero, qui en contient un, est dans le PATH. Sur une machine
sans Delphi, l'exe refusait de demarrer.

C'est le Win64 qui l'a revele — la DLL du PATH est 32 bits — mais le
Win32 livre etait casse de la meme facon chez quiconque n'a pas Delphi,
paquet prepare pour un tiers compris.

Prouve avec un PATH reduit a Windows : avec l'unite SQLite s'ouvre
(3.42.0), sans elle l'erreur exacte. Lire la table d'import ne
suffisait pas, le chargement est dynamique.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-16 23:15:42 +01:00
r-zakarya 0d4d4d518c build: activer Win64, dont la configuration etait vide
Le groupe Base_Win64 du .dproj n'avait ni prefixes d'unites — d'ou
"Unite 'Windows' non trouvee" en recompilant TMS — ni Cfg_1_Win64 :
l'exe aurait compile sans icone et sans PerMonitorV2, donc flou sur un
ecran haute resolution. Complete en miroir exact de Win32, et Win64
coche dans l'IDE.

Builds Release 32 et 64 bits produits sur E:, un dossier .dcu par
plateforme — c'est ce qui empeche la pollution des sources TMS qui avait
casse le build Win32 le 7 septembre. Verifie : aucun .dcu depose.

Empreinte de licence identique en 32 et 64 bits, les deux couches a
TRUE des deux cotes : une licence vaut pour les deux exe.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-16 23:01:25 +01:00
r-zakarya 72b776c26e docs: le dernier caractere de controle, que le commit precedent disait absent
\a s'etait change en BEL dans E:\a pmanager. Le commit precedent
affirmait qu'il ne restait plus rien sans l'avoir mesure apres coup —
c'est ce qui l'a laisse passer. Mesure cette fois : zero.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-16 22:56:44 +01:00
r-zakarya 5d51614a1b docs: reparer deux chemins que le heredoc avait corrompus
\t et \r s'etaient changes en tabulation et en retour chariot dans les
chemins du piege TMS. Verifie apres coup : plus aucun caractere de
controle parasite dans le fichier.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-16 22:56:23 +01:00
r-zakarya 1fa5113c02 feat(licence): deuxieme couche, l'API refuse sans licence valable
L'ecran d'activation ne consultait qu'un branchement : le rapiecer
ouvrait tout, l'API ne demandant rien. Chaque route de PM.Router passe
maintenant par PM.LicenceGate.ApiLicenceOk avant son handler.

Aucune fonction de decision partagee avec PM.Licence — sinon un seul
rapiecage ouvrirait les deux couches. La verification est refaite de
bout en bout, appels CNG redeclares compris ; seules la cle publique et
le nom de la pref sont partages, parce que ce sont des donnees.

Branchee dans DispatchRequest : les fichiers de l'ecran d'activation
passent par TryServeEmbedded, non garde, sinon l'ecran ne se chargerait
plus. Un oui est retenu pour le processus, un non jamais.

Le risque etait qu'elle refuse un client licencie. Auto-test sur un
vecteur lie a une empreinte fixe, sens accepte en premier ; et accord
verifie en conditions reelles sur ce poste : IsLicensed = TRUE,
ApiLicenceOk = TRUE.

Documente aussi le piege du jour, sans lien avec ce code : des .dcu TMS
x64 deposes a cote des sources cassaient le build Win32, et la sortie
redirigee vers E: faute de place sur Z:.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-16 22:55:33 +01:00
r-zakarya 5722347e75 docs: le depot a un remote, contrairement a ce qui etait ecrit
origin pointe sur le Gitea auto-heberge. CLAUDE.md affirmait "pas de
remote configure (dev local)", ce qui a cesse d'etre vrai sans que le
document suive — 427 commits d'avance au moment du constat.

Rien n'est pousse automatiquement, et c'est note : lire "il y a un
remote" ne doit pas se comprendre comme "c'est sauvegarde ailleurs".

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-06 19:30:32 +01:00
r-zakarya 370d35f69a docs(licence): l'APK reutilisera la licence desktop, pas la sienne
Differe volontairement : l'APK ne part chez personne aujourd'hui. Le
jour venu, l'instantane publie par le poste portera la licence et le
telephone exigera d'y en trouver une — il le lit deja, donc aucun
nouveau format, aucune empreinte Android, aucune emission de plus.

Les trois raisons de ne pas refaire un second dispositif sont notees
pour ne pas les re-deriver : Java attend du DER la ou CNG accepte r||s
brut, ANDROID_ID est remis a zero par une reinitialisation d'usine (donc
des clients legitimes refuses), et un APK se repaquette en dix minutes.

Et l'APK n'aura PAS a verifier la signature, seulement la presence :
payer le DER et le JNI pour durcir un controle que le repaquetage retire
serait le mauvais echange.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-05 19:13:12 +01:00
r-zakarya 253e81252c docs(licence): scenario d'essai, et le bouton qui le rend jouable
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>
2026-09-05 18:06:45 +01:00
r-zakarya fc945ac6ab fix(licence): nouvelle paire, et la privee ne nait plus dans le depot
La premiere privee a ete supprimee sans copie : plus aucune licence
nouvelle n'etait emettable. Regeneration + reemission, indolore
aujourd'hui parce qu'une seule licence circulait.

licence.ps1 ecrit desormais la cle dans %USERPROFILE%\Documents, hors du
depot. Elle vivait dans tools\, ou elle n'attendait qu'un menage : le
.gitignore la protegeait du depot, rien ne la protegeait de son
proprietaire.

Verifie : la licence de la nouvelle paire passe, celle de la paire
perdue tombe — la regeneration a bien remplace, pas ajoute.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-05 17:50:14 +01:00
r-zakarya 427d294b22 feat(assets): brouiller les blocs embarques dans l'exe
Le frontend etait lisible dans l'onglet Image de Process Explorer, avec
les commentaires. BuildAssets.ps1 brouille chaque fichier avant brcc32,
PM.EmbeddedAssets le debrouille au moment de servir.

C'est de l'opacite, pas du secret : la cle est dans l'exe. Ca supprime
strings et l'onglet Image ; la memoire, un debogueur et la reponse HTTP
rendent toujours le texte. D'ou XOR plutot qu'AES — a securite egale
(nulle contre qui a l'exe), dix lignes, et aucune force affichee qu'on
n'a pas.

Une magie PMA1 prefixe le clair : sans elle, deux constantes qui derivent
serviraient du binaire au navigateur, ecran blanc sans trace.

Verifie en comparant les flux des deux cotes bloc par bloc, puis par
aller-retour sur app.js, index.html et style.css. Le frontend ne se
trouve plus dans le binaire.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-05 17:17:15 +01:00
r-zakarya 8f0bbb580f feat(licence): Settings affiche le porteur de la licence
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>
2026-09-05 16:19:24 +01:00
r-zakarya 61ec5ff7a1 fix(licence): l'empreinte ne depend plus de l'ordre de demarrage
BoardUUID s'appuyait sur un COM initialise par quelqu'un d'autre. Appele
apres WebView2 il lisait le vrai UUID de carte mere, appele avant il
echouait et retombait sur MachineGuid : deux empreintes pour une seule
machine, et une licence liee a l'une que l'autre refuse. Il suffisait que
licence/status parte avant l'init du navigateur pour verrouiller
l'utilisateur hors de son coffre.

CoInitializeEx est desormais appele sur place. Pas de CoUninitialize
symetrique : essaye, il rend une violation d'acces — les mandataires WMI
et le cache de type-info de ComObj se liberent plus tard, dans un
appartement qu'on vient de fermer.

Verifie avec un harnais qui n'initialise PAS COM : il rend maintenant la
meme empreinte que l'application.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-05 16:00:50 +01:00
r-zakarya 988694b20d feat(licence): refuser les licences non liees a un poste
Une cle flottante etait sortie. Un schema hors ligne n'a pas de
revocation — rien ne peut aller dire a un exe installe qu'une cle ne vaut
plus — donc la seule prise est de cesser d'accepter toute la classe.

MachineMatches exige desormais une empreinte, et -Floating disparait du
script : il ne peut plus fabriquer une cle que l'exe rejetterait.

Effet de bord assume : plus de licence de site, et un poste sans
empreinte exploitable n'est plus licenciable du tout — mieux qu'une
empreinte degeneree qui ouvrirait partout.

Verifie : liee a ce poste acceptee, liee a un autre refusee, les deux
flottantes deja emises refusees.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-05 15:38:07 +01:00
r-zakarya 405ac7fac4 feat(licence): lier la licence au poste (carte mere + disque)
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>
2026-09-05 15:16:35 +01:00
r-zakarya cd6e70449d feat(licence): ecran d'activation au demarrage
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>
2026-09-05 14:58:19 +01:00
r-zakarya 960d2b388a feat(licence): verification hors ligne d'une licence signee
PM.Licence verifie une chaine charge-utile.signature contre la cle
publique de l'editeur, via BCryptVerifySignature. Aucun ASN.1 : la
signature du script est deja en r||s brut et les coordonnees se rangent
telles quelles dans un BCRYPT_ECCPUBLIC_BLOB.

La cle publique est livree A ZERO — tant que la sortie de licence.ps1
-Init n'y est pas collee, aucune licence n'est valide. C'est le defaut
sur : un blob par defaut qui accepterait quelque chose serait pire.

IsLicensed REVERIFIE la licence rangee plutot que de lire un drapeau.
La signature est verifiee AVANT que la charge utile soit analysee.

SelfTestLicence a l'initialization rejoue un vecteur fige signe par une
paire de test : valable accepte, un octet retourne refuse. Verifie en
executant l'unite, pas seulement en la compilant.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-05 14:44:55 +01:00
r-zakarya fc03c90c95 fix(settings): le toggle autofill survit au changement de port
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>
2026-09-05 13:04:42 +01:00
r-zakarya bf9b62a541 feat(server): un port occupe ne tue plus le demarrage
Le lien n'etait garde par rien : un port pris levait, et l'application
s'ouvrait sur une boite d'erreur. Trois essais desormais - le premier
honore le port de config.txt, que l'utilisateur a peut-etre choisi
expres, les suivants prennent un ephemere.

SE LIER EST LE TEST. Sonder le port avant serait plus faible - un service
qui ecoute sans repondre passerait inapercu - et sujet a une course : le
port peut etre pris entre le sondage et le lien. Chaque deplacement est
journalise, un serveur qui change de port en silence etant pire qu'un
serveur qui refuse de demarrer.

Trouve en chemin : Randomize n'est appele NULLE PART, donc le port
"aleatoire" de port=0 etait le meme a chaque lancement - l'aleatoire cense
compliquer un balayage exterieur n'existait pas. Il vient maintenant de
RandomBytes, donc de BCryptGenRandom, deja utilise pour le jeton d'acces.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-05 08:45:32 +01:00
r-zakarya f8b7ffe379 refactor(autofill): "Fill bar" plutot que "Lazy mode"
Le premier nomme la chose, le second decrivait l'utilisateur. Le signe
qui a tranche : l'ancien libelle avait besoin d'une parenthese pour se
faire comprendre - "Lazy mode (fill with the mouse)" - et c'etait elle
qui portait le sens.

Seul le libelle change. Les identifiants internes gardent "lazy" -
FLazyWnd, LAZY_*, et surtout les prefs lazyOn / lazyPos / lazyAlphaPct :
renommer ces cles perdrait les reglages deja poses sur la machine.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-29 01:03:06 +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
r-zakarya ebcd75fd2e feat(backup): include the trash in exports and auto-backups
A backup that silently drops the trash is not a backup: export, empty the vault, import, and the trashed entries were gone for good with nothing said. Both containers now carry state.trashed with deleted/deleted_at, and deleted_at is preserved so a restore resumes the auto-purge clock instead of restarting it - hence its addition to the GET echo.

The sync snapshot deliberately does NOT: the trash is device-local, and re-publishing deleted rows is what produced the duplicate-uuid bug. The two identical export loops were folded into buildBackupEntry first, so the trash handling exists once.
2026-08-07 15:04:41 +01:00
r-zakarya e919a79522 docs(claude): document the multiline Notes field and the custom-field row 2026-08-01 14:00:45 +01:00
r-zakarya 415dc6c8ba docs(claude): record that pre-login settings must live in DPAPI prefs 2026-07-28 22:51:00 +01:00
r-zakarya 7c86f35fce feat(autofill): Ctrl+Shift+T types the 2FA code
Same workflow as the password-only hotkey: click the 2FA field, press the
combo, the current 6-digit code is typed in.

Reuses the whole autofill pipeline — no new native typing path. The code is
sent in the password slot with an empty username, so Delphi's existing
password-only branch types just the code.

- Bridge: akTotp kind, hotkey id 47, registered/torn down alongside the other
  two autofill combos; an unconfigured combo (vk=0) doesn't count as a
  registration failure.
- Matching filters on totp_secret BEFORE scoring, so the multi-match picker
  never offers an entry that has no code.
- Configurable + synced like the others (autofillHotkeyTotp), collision-checked
  against the other four slots, covered by Reset to defaults and the cheatsheet.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-25 16:53:00 +01:00