91 Commits

Author SHA1 Message Date
r-zakarya 092eb2f73a feat(fillbar): bouton Paste qui tape le presse-papiers Windows
Pour les clients a distance qui ne transmettent pas le presse-papiers
(TigerVNC vers un Mac) : le texte est tape par le chemin de l'autofill,
touches VNC et dispositions comprises, insere au curseur. Et l'arabe des
champs a chasse fixe s'affiche a la bonne taille (repli Segoe UI / Tahoma).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 17:53:09 +01:00
r-zakarya a0527eb806 feat(quicksearch): bouton retour visible depuis une note de commandes
Fleche a la place de la loupe et Retour arriere sur champ vide, en plus
d'Esc qui ne se voyait pas.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 18:38:56 +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 28349c10c8 fix(quick-search): Esc ne sort plus du cadre, et la note dit qu'elle se copie
Le champ de recherche gardait la largeur de son texte d'aide (min-width par
defaut) et poussait l'interrupteur et Esc hors du panneau. Le texte d'aide
parlait aussi de copier alors que Fill est souvent actif : il dit "Search..."
tout court. Et le bouton d'une note precise, en mode Fill, qu'une note n'est
jamais tapee.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 12:12:06 +01:00
r-zakarya 7c26716831 feat(quick-search): interrupteur Copy / Fill quand une fenetre cible existe
Ouverte par Ctrl+Shift+Q ou la barrette, la recherche remplissait toujours ;
copier restait impossible alors que le remplissage echoue vers une application
lancee en administrateur, ou quand le gestionnaire de Chrome s'en mele. Sans
cible (tray, palette), l'interrupteur n'apparait pas : remplir n'aurait rien a
viser. Le choix est retenu en pref DPAPI, les verbes des boutons et l'aide
suivent.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 12:07:57 +01:00
r-zakarya 652815ea98 feat(quick-search): boutons par entree - utilisateur, mot de passe ou note, code 2FA (s'ils existent)
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-18 23:15:49 +01:00
r-zakarya ab1da7e319 feat(autofill): etiquette no-autofill pour exclure une entree, copie du mot de passe toujours visible en vue tableau
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-18 15:22:14 +01:00
r-zakarya d8f4f19041 fix(licence): Register ne clignote plus avant d'etre masque
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>
2026-09-17 06:08:37 +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 2cb7d29450 fix(licence): une cle refusee s'affiche en rouge
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>
2026-09-05 15:09:04 +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 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 7074e7ff93 fix(list): keep the header gap constant when it sticks
offsetHeight feeds --hdr-h, where the sticky pagination parks, and it ignores margins - so the 20px gap under the title vanished as soon as the header stuck. Turned into padding: measured and painted, so the spacing stays constant and opaque.
2026-08-02 00:09:33 +01:00
r-zakarya 4c734de266 fix(list): give the content header a fixed height
min-height still let a child grow the row, and a couple of stray pixels from the selection bar pushed the grid down in table and list view. A fixed height makes overflow purely visual - nothing inside can move what follows.
2026-08-02 00:04:52 +01:00
r-zakarya 29ba069659 fix(list): pin the selection bar to the height the header reserves
It was a few pixels taller than the reservation, so selecting anything pushed the whole grid down - the shift moving the bar into the header was supposed to remove.
2026-08-01 23:56:15 +01:00
r-zakarya fb65f4b7d6 fix(hibp): put the progress pill in the toast stack, bottom right
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.
2026-08-01 22:41:34 +01:00
r-zakarya 961d77534f fix(hibp): move the progress pill off the sidebar and abort the scan on toggle-off
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.
2026-08-01 20:12:09 +01:00
r-zakarya fd03c1da25 fix(list): keep the sticky bars above the row checkboxes
The pagination bar and the table header sat at or below the checkbox z-index, and at equal values the later DOM element wins - the cards come after both, so checkboxes of rows scrolling underneath were painted over them. Layers are now explicit: check 2, table header 3, pagination 4, content header 12.
2026-08-01 20:05:51 +01:00
r-zakarya c9068a6732 feat(hibp): show a progress pill while the breach scan runs
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.
2026-08-01 19:58:16 +01:00
r-zakarya 6bee69eb1a feat(health): show the per-category breakdown under the score
Each category is capped, so past the cap fixing one more password leaves the total unchanged and the work looks wasted. The breakdown lists count and points per category, marks a capped deduction with (max), and explains in a tooltip why the score is standing still.
2026-08-01 19:32:37 +01:00
r-zakarya 4936aa78ca refactor(entries): drop the cyan secret marker on the reveal eye
The eye only renders on a secret field, so its presence already carries the state.
2026-08-01 12:47:59 +01:00
r-zakarya a8c1a88ec4 refactor(entries): drop the inline copy button, the row menu carries it
Row is down to label | value (eye inside) | menu.
2026-08-01 12:05:44 +01:00
r-zakarya e5758f984c fix(entries): keep the reveal eye clear of the multiline scrollbar
scrollbar-gutter reserves the track whether or not it is showing, so the eye keeps the same position once the content passes the auto-grow cap.
2026-08-01 12:04:46 +01:00
r-zakarya a2cd08c676 feat(entries): move the reveal eye inside the custom-field value
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.
2026-08-01 11:57:56 +01:00
r-zakarya a0a069c7f6 refactor(entries): fold the secret toggle into a per-field row menu
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.
2026-08-01 11:47:25 +01:00
r-zakarya 9dc9f625d6 fix(entries): size the multiline field from its content instead of a fixed height
A manual resize was lost on reopen: the row is rebuilt on every open, and remembering the height would not survive a restart either since localStorage is wiped when the ephemeral port changes the origin. Deriving it from the content removes the state entirely - the field is always the right size.
2026-07-31 18:31:26 +01:00
r-zakarya d8643b96d1 feat(entries): multiline Notes field for any entry, masked by default
The note body of a note entry is the encrypted_password column, so logins had no way to carry notes. Rather than a dedicated encrypted column and the seven call sites that come with it, this rides on custom_fields, which rotation, duplicate, export/import and sync already carry.

Both the open-time normalisation and the save-time rebuild drop any key they do not name, so multiline is carried explicitly in each - otherwise the field silently reopens single-line.
2026-07-28 23:06:39 +01:00
r-zakarya 3513e7b6e5 fix(ui): actionable toasts linger, and a dead hotkey shows in the sidebar
Every toast auto-dismissed after 2.8 s — fine for a success receipt, too
short to read AND act on an error. Errors and warnings now stay 10 s and can
be clicked away; success is unchanged.

The hotkey-conflict warning also needed a trace outside the toast: the ⚠ in
Settings only helps someone who opens the panel. A dot on the sidebar Settings
entry (plus a tooltip) is now the cue in the main UI that a combo is dead.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-25 18:10:17 +01:00
r-zakarya 4965ff2518 feat(hotkeys): persistent conflict marker in Settings
A toast is gone by the time the user opens Settings, so a hotkey Windows
refused looked perfectly configured while doing nothing. The offending row now
carries a ⚠ marker (amber border + tooltip explaining the combo is taken)
until a re-registration clears it.

Delphi reports slot ids instead of labels; HOTKEY_SLOTS maps id → button +
label in one place, so JS owns the wording and can flag the exact row. The
markers are re-applied by openSettings, surviving panel re-renders.

Also stop toasting success on the startup push (it fires on every unlock) —
autofillPushHotkeys(true). A clash still warns: that one is actionable.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-25 17:52:05 +01:00
r-zakarya 0434eec85e refactor: remove the dead legacy entry modal
openEntryModal had zero callers — the slideover editor replaced it — but the
whole cluster still shipped: the #entryModal markup, saveEntry (wired to the
form submit), closeEntryModal, populateFolderSelect, updateEntryStrength, and
the entire tag-chip subsystem (editingTags, renderChips, addTag, the suggest
dropdown) plus their init wirings and the Esc-chain branch.

saveEntry was a latent hazard: still bound to #entryForm submit, it did a
PARTIAL PUT (no kind / totp_secret / custom_fields / template) — the exact
silent-data-loss bug moveEntryToFolder documents. Unreachable today, but one
stray call away from wiping fields.

Removed JS + HTML + the orphaned .chip-suggestion CSS. The generator is shared
with the slideover — kept; its dead 'entry' target branch collapsed to
slideover-only. 322 lines gone, tests green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-25 14:35:39 +01:00
r-zakarya 6f4363bde6 fix(batch): move the selection bar into the title row
Selecting an entry inserted the batch bar as a block above the grid, pushing
every row down ~60px — the list jumped under the cursor mid-click. The bar
now renders inside .content-header, to the right of "All items", and the
header reserves its height (min-height) so appearing and disappearing shifts
nothing at all.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-23 15:59:53 +01:00
r-zakarya 3f07c1217b feat(notes): eye toggle to mask the note body in the editor
The note textarea showed its plaintext to anyone glancing at the screen,
unlike every other secret in the editor. An eye button on the NOTE label
masks it with -webkit-text-security (a textarea has no type="password").

The button IS the setting: it flips state.maskNotes, which is synced via
settings_json, so the choice sticks across entries and devices — no extra
Settings row for a one-bit preference.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-22 20:16:06 +01:00
r-zakarya 72ef034459 fix(list): stop the folder/tag chips drifting row to row
.entry-meta and the first order-6 action both had margin-left:auto, so the
free space was SPLIT between them — the chips landed at a different x on
every row depending on how wide the rest of the row was. .entry-meta is
always present (empty when the entry has no chips), so its auto alone puts
the actions at the far edge and right-aligns the chips against them.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-22 11:56:48 +01:00
r-zakarya a0b6c5c67a style(list): note rows get the same 110px track as password rows
The note placeholder was still flex:1, so note rows' copy button sat at the
far edge while password rows' sat at 110px. Same fixed track for both, full
label on hover.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-22 11:53:25 +01:00
r-zakarya 506230f138 fix(list): shrink the password pill and tooltip truncated names
The list-view rule set `width: 110px`, but the base .entry-pw is `flex: 1`
(basis 0), so width was ignored and the pill stretched the full row width —
dragging the copy/open buttons to the far edge. `flex: 0 0 110px` pins it,
and the actions land right after it.

Names that ellipsise in the fixed-width title column (list) and the name
cell (table) now carry a title attr, so the full name shows on hover.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-22 11:50:52 +01:00
r-zakarya bef739b7ca fix(layout): stop rows showing through above the sticky title
A sticky box is held so its *margin* box respects `top`, so the -24px
margin-top used to cancel .content's padding pushed the header's border box
24px down from the scrollport edge — rows scrolled through the strip above
it. Move the top padding off .content and onto .content-header instead: same
spacing at rest, opaque box flush with the scrollport when stuck.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-22 11:43:57 +01:00
r-zakarya 24d74df2ab feat(layout): stick the title row too, and chain the sticky offsets
"All items · N items" scrolled away like the pager did. It now sticks at
the scrollport top (padding-top/negative margin cancel .content's padding
so nothing shows through the gap), and the three things below it offset
by --hdr-h: batch bar, pagination, and the table header (--hdr-h + --pag-h).
Both vars are measured on .content in renderEntries.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-22 11:31:52 +01:00
r-zakarya 60cf26b259 feat(pagination): keep the pager visible while scrolling
The page buttons sat at the top of the grid and scrolled away, so paging
through a long list meant scrolling back up. Sticky at the scrollport top
with an opaque background; the table header now sticks below it via
--pag-h (measured after append, so a wrapped pager still lines up).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-22 11:22:50 +01:00
r-zakarya 4dfd908a9a style(table): move the column picker into the table header corner
The show/hide-columns gear only makes sense in table view but sat in the
global toolbar (needing applyColsWrapVisibility to hide it elsewhere). Build
it inside the actions-column header cell in renderTable instead, with inline
handlers (recreated each render, so no dependence on once-wired listeners on
a static node). Removed #colsWrap from the toolbar + the now-obsolete
applyColsWrapVisibility and its callers; kept the menu id so
renderTableColsMenu still populates it, and a live-queried outside-click close.
Added .cols-wrap CSS (was an inline position:relative on the old node).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-21 20:00:56 +01:00
r-zakarya 0404fc65a4 style(list): align the password column across rows
List view faked a table with flex+order, but the title used flex:1 so it
absorbed all free space — and since trailing content (tags/folder/actions)
varies per row, the fixed-width pw pill landed at a different x on each row.
Give the title a fixed 240px track so pw starts at the same x everywhere;
move the free-space absorption to margin-left:auto on the trailing group
(entry-meta + order-6 actions) so they still float to the far right. Copy +
globe stay in .entry-pw-row (order 4), pinned next to the pw.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 21:58:46 +01:00
r-zakarya a42e4b205d feat(entries): encrypt template at rest, guided tour, import fixes, cleanup
Batched session work sharing app.js / index.html / Entries.pas, so it can't
split cleanly without interactive hunk staging.

- feat: encrypt `template` metadata at rest (template_enc/iv, added to
  ENCRYPTED_META_FIELDS). withEncryptedMeta skips an absent template key so
  partial re-ships (add-tag, move-to-folder) don't wipe it via LHasTemplate.
  Cleartext column kept as migration fallback. +3 unit tests.
- feat: first-run guided tour ("How it works") — spotlight + bubble, no GIFs,
  re-launchable from Settings, seen-flag in DPAPI prefs.
- fix(import): preserve original created_at on restore (was stamped to import
  time); restore entry icons on overwrite (PUT ignores icon_b64).
- fix(settings): correct clipboard-privacy copy (already excluded from Win+V);
  PIN text 4-6 -> 4-12; reorder Set-PIN above unlock-method; move tray/startup
  toggles to General; dedicated backup-password button + warning status; tab icons.
- chore: remove dead legacy monolith (app-legacy.js, index-legacy.html,
  style-legacy.css) + unused passkeyBtn stub.
- docs: full-source review (CODE_AUDIT 6b), template + favorite/pinned notes.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-11 18:32:37 +01:00
r-zakarya 45ba47f772 feat(settings): keep backup pwd on disable, dedicated pwd button, tab icons
Auto-backup no longer wipes the stored password when disabled, so
re-enabling reuses it silently. A dedicated "Set/Change backup password"
button (mirrors sync) owns the password, with a warning status when
unset. Corrected the stale hint that claimed the backup pwd was derived
from the master password. Added icons to each settings tab.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-10 16:53:45 +01:00
r-zakarya 8d7da02b0d fix(settings): force 800px width in centered mode (center rule was 520) 2026-07-10 11:11:59 +01:00
r-zakarya e6922d5b19 style(settings): centered modal height 720 (min(94vh,720px)) 2026-07-10 05:23:01 +01:00
r-zakarya c109694bd1 style(settings): size panel to 800x600 (viewport-guarded) 2026-07-10 05:18:02 +01:00
r-zakarya a0a6fa8834 fix(settings): widen the settings panel for the left tab column
Side-mode settings panel (420px) clipped its content (Clear cache button) now
that the vertical tab column eats ~100px. Bumped #settingsPanel to 520px (id
beats .slideover's 420; entry #slideover unchanged). Nudged centered height
620→660.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-10 05:14:49 +01:00
r-zakarya c59a595d1d feat(settings): fixed-height modal + move unlock sections to Account
- Centered settings modal gets a fixed height (min(88vh,620px)) so switching
  between short/long tabs no longer resizes + re-centers it. Entry #slideover
  stays content-sized (edit/new unaffected).
- PIN unlock / Quick unlock / Recovery key moved from Sécurité to the Account
  tab (unlock methods live with the account). Autofill stays in Sécurité.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-10 05:09:11 +01:00
r-zakarya 18533486b7 style(settings): move tabs to a vertical column on the left
Wrapped the tab bar + body in a .settings-main flex row; tabs now stack
vertically on the left with a right border, body scrolls on the right. JS
unchanged (toggles .is-tab-hidden on sections regardless of layout).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-10 04:56:16 +01:00
r-zakarya bec94930b4 feat(settings): group settings into 4 tabs
Long settings panel → 4 tabs (Général / Sécurité / Account / Sync & Backup).
Each section (.slideover-field) is keyed by its label text to a tab via
SETTINGS_TAB_OF; applySettingsTab toggles .is-tab-hidden on the rest. No HTML
restructure (sections were already .slideover-field siblings), no dep. The
existing settings search composes: a live query suspends the tab filter so
cross-tab matches show, clearing it restores the active tab.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-09 23:42:22 +01:00