From d7a8b4ddffb472d3fecc03dad023a764fb6c9baa Mon Sep 17 00:00:00 2001 From: r-zakarya <82443831+r-zakarya@users.noreply.github.com> Date: Fri, 18 Sep 2026 10:24:26 +0100 Subject: [PATCH] feat(fillbar): onglet Fill bar dans les reglages, icones ou texte, boutons Add et Search - 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 --- delphi-backend/Source/PM.Bridge.pas | 246 +++++++++++++++++++++------- delphi-backend/UMainForm.pas | 36 ++++ index.html | 48 ++++++ js/app.js | 32 ++++ 4 files changed, 302 insertions(+), 60 deletions(-) diff --git a/delphi-backend/Source/PM.Bridge.pas b/delphi-backend/Source/PM.Bridge.pas index 67d069f..7386ff7 100644 --- a/delphi-backend/Source/PM.Bridge.pas +++ b/delphi-backend/Source/PM.Bridge.pas @@ -169,7 +169,15 @@ type // Dernier etat connu du survol, pour ne reposer l'opacite que sur un // changement - SetLayeredWindowAttributes force un repaint a chaque appel. FLazyHover: Boolean; + // Barrette en icones (police Segoe MDL2 Assets) plutot qu'en mots, et sa + // largeur reelle - elle depend des boutons choisis dans les reglages. + FLazyIcons: Boolean; + FLazyW: Integer; procedure LazyWindowHandler(var AMsg: TMessage); + // Corps extraits des raccourcis Ctrl+Shift+A et Ctrl+Shift+Q : la barrette + // les declenche aussi, meme capture de la fenetre cible. + procedure FireNewEntryRequest; + procedure FireQuickSearchRequest; /// Pose l'opacite qui convient a l'etat de survol donne. procedure ApplyLazyAlpha(AHover: Boolean); /// Menu du clic droit sur la barrette : le choix de l'opacite au repos. @@ -271,6 +279,10 @@ type // peinture a besoin du theme. procedure RestoreLazyBar; function LazyBarVisible: Boolean; + /// Reconstruit la barrette apres un changement de ses reglages (boutons, + /// icones). POSTE : l'appel peut venir de la barrette elle-meme, qu'on ne + /// detruit pas depuis son propre gestionnaire. + procedure ReloadLazyBar; property AutofillRegistered: Boolean read FAutofillRegistered; // Per-slot outcome of the last SetAutofillHotkeys — lets the host name // exactly which combo lost the race with another app's global hotkey @@ -329,7 +341,7 @@ type implementation uses - FMX.Platform.Win; + FMX.Platform.Win, Winapi.CommCtrl; // Foreground window at process start — captured in this unit's initialization, // which runs before any of our windows exist. Every later opportunity is @@ -377,6 +389,7 @@ const // traitee sur place : detruire une fenetre depuis son propre gestionnaire // libererait, en plein envoi, l'instance qui sert de window proc. WM_LAZY_CLOSE = WM_APP + 2; + WM_LAZY_RELOAD = WM_APP + 3; // WTS session change message and state constants (declared here to avoid // a hard dependency on Winapi.WtsApi32 which varies across Delphi versions). @@ -420,6 +433,9 @@ const LAZY_ID_TOTP = 103; LAZY_ID_CLOSE = 104; LAZY_ID_OPEN = 105; + LAZY_ID_ADD = 106; // nouvelle entree depuis la fenetre en avant (Ctrl+Shift+A) + LAZY_ID_SEARCH = 107; // recherche rapide (Ctrl+Shift+Q) + LAZY_MENU_ICONS = 1; // hors des valeurs d'opacite (25..100) du meme menu // LE SONDAGE PLUTOT QUE TrackMouseEvent. La barrette a quatre fenetres // enfants : passer d'elle a un bouton envoie deja un WM_MOUSELEAVE au parent, // et il faudrait donc suivre la souris sur les cinq fenetres pour recoller @@ -438,7 +454,11 @@ const // elle doit revenir AVANT toute ouverture de coffre, et localStorage est vide // a chaque lancement (le port ephemere change d'origine). LAZY_ON_KEY = 'lazyOn'; - LAZY_W = 298; + // Boutons affiches, dans l'ordre, et le choix icones/texte. Le X est toujours + // la : c'est la seule facon de fermer la barrette la ou elle est. + LAZY_BTNS_KEY = 'lazyButtons'; + LAZY_BTNS_DEFAULT = 'full,pwd,totp,add,search,open'; + LAZY_ICONS_KEY = 'lazyIcons'; LAZY_H = 40; // Palette de la barrette, en COLORREF ($00BBGGRR - les octets sont INVERSES @@ -1142,6 +1162,9 @@ var LBrush: HBRUSH; LFace, LEdge, LInk: COLORREF; LText: array[0..31] of Char; + LGlyph: Char; + LFont: HFONT; + LOldFont: HGDIOBJ; begin LRect := ADraw.rcItem; if FDarkTheme then @@ -1162,11 +1185,35 @@ begin FrameRect(ADraw.hDC, LRect, LBrush); DeleteObject(LBrush); + SetBkMode(ADraw.hDC, TRANSPARENT); + SetTextColor(ADraw.hDC, LInk); + if FLazyIcons then + begin + // Segoe MDL2 Assets, livree avec Windows 10 et 11 : aucune image a + // embarquer, et le glyphe suit la couleur du texte comme un mot. + case ADraw.CtlID of + LAZY_ID_FULL: LGlyph := #$E77B; // Contact + LAZY_ID_PWD: LGlyph := #$E8D7; // Permissions (cle) + LAZY_ID_TOTP: LGlyph := #$E823; // Recent (horloge du code) + LAZY_ID_ADD: LGlyph := #$E710; // Add + LAZY_ID_SEARCH: LGlyph := #$E721; // Search + LAZY_ID_OPEN: LGlyph := #$E8A7; // OpenInNewWindow + else + LGlyph := #$E711; // Cancel (X) + end; + LFont := CreateFont(-16, 0, 0, 0, FW_NORMAL, 0, 0, 0, DEFAULT_CHARSET, + OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, CLEARTYPE_QUALITY, + DEFAULT_PITCH, 'Segoe MDL2 Assets'); + LOldFont := SelectObject(ADraw.hDC, LFont); + DrawText(ADraw.hDC, PChar(LGlyph), 1, LRect, + DT_CENTER or DT_VCENTER or DT_SINGLELINE); + SelectObject(ADraw.hDC, LOldFont); + DeleteObject(LFont); + Exit; + end; // Le libelle est celui de la fenetre-bouton : une seule verite, posee a la // creation, plutot qu'une table de correspondance a tenir a jour. GetWindowText(ADraw.hwndItem, LText, Length(LText)); - SetBkMode(ADraw.hDC, TRANSPARENT); - SetTextColor(ADraw.hDC, LInk); DrawText(ADraw.hDC, LText, -1, LRect, DT_CENTER or DT_VCENTER or DT_SINGLELINE); end; @@ -1191,6 +1238,7 @@ var LPt: TPoint; LCmd: Cardinal; I: Integer; + LIconsLabel: string; begin LMenu := CreatePopupMenu; if LMenu = 0 then Exit; @@ -1199,6 +1247,9 @@ begin AppendMenu(LMenu, MF_STRING or IfThen(FLazyAlphaPct = CHOICES[I], MF_CHECKED, MF_UNCHECKED), CHOICES[I], PChar(Format('%d%% when the mouse leaves', [CHOICES[I]]))); + if FLazyIcons then LIconsLabel := 'Show text' else LIconsLabel := 'Show icons'; + AppendMenu(LMenu, MF_SEPARATOR, 0, nil); + AppendMenu(LMenu, MF_STRING, LAZY_MENU_ICONS, PChar(LIconsLabel)); // LE BATTEMENT DOIT S'ARRETER PENDANT LE MENU. Il repose la barrette en // tete de la bande TOPMOST cinq fois par seconde - or le menu deroulant est @@ -1216,7 +1267,12 @@ begin LPt.X, LPt.Y, 0, FMsgWindow, nil)); PostMessage(FMsgWindow, WM_NULL, 0, 0); - if LCmd > 0 then + if LCmd = LAZY_MENU_ICONS then + begin + if FLazyIcons then SetPref(LAZY_ICONS_KEY, '0') else SetPref(LAZY_ICONS_KEY, '1'); + ReloadLazyBar; + end + else if LCmd > 0 then begin FLazyAlphaPct := LCmd; SetPref(LAZY_ALPHA_KEY, IntToStr(FLazyAlphaPct)); @@ -1317,6 +1373,8 @@ begin LAZY_ID_PWD: FireAutofillRequest(akPasswordOnly, True); LAZY_ID_TOTP: FireAutofillRequest(akTotp, True); LAZY_ID_OPEN: if Assigned(FOnTrayRestore) then FOnTrayRestore(); + LAZY_ID_ADD: FireNewEntryRequest; + LAZY_ID_SEARCH: FireQuickSearchRequest; LAZY_ID_CLOSE: PostMessage(FMsgWindow, WM_LAZY_CLOSE, 0, 0); end; AMsg.Result := 0; @@ -1326,20 +1384,84 @@ begin AMsg.Result := DefWindowProc(FLazyWnd, AMsg.Msg, AMsg.WParam, AMsg.LParam); end; +procedure TPMBridge.FireNewEntryRequest; +var + LTarget: HWND; + LTitle: string; + LLen: Integer; +begin + if not Assigned(FOnNewEntryHotkey) then Exit; + LTarget := GetForegroundWindow; + LTitle := ''; + // The window we are about to cover is exactly the one to hand the + // foreground back to when we return to the tray. + CaptureForegroundOwner; + // From the tray the foreground can resolve to OUR own window — don't + // pre-fill the new entry with "Password Manager". Only capture a real + // external window's title; otherwise pass '' (blank title). + if not IsOwnProcessWindow(LTarget) then + begin + SetLength(LTitle, 512); + LLen := GetWindowTextW(LTarget, PChar(LTitle), 512); + SetLength(LTitle, LLen); + end; + FOnNewEntryHotkey(LTitle); +end; + +procedure TPMBridge.FireQuickSearchRequest; +var + LTarget: HWND; + LTitle: string; + LLen: Integer; +begin + // Quick-search + autofill: capture the foreground HWND BEFORE the JS + // modal steals focus, hand it to the host so it can stash it in + // FAutofillTargetHWND (consumed by cmd://autofill/execute later). + if not Assigned(FOnQuickSearchHotkey) then Exit; + LTarget := GetForegroundWindow; + CaptureForegroundOwner; + // Same trap as the autofill hotkeys: our own hidden window can be the + // foreground one. Report no target — the host then opens quick search in + // copy mode instead of offering to type into ourselves. + if IsOwnProcessWindow(LTarget) then LTarget := 0; + LTitle := ''; + if LTarget <> 0 then + begin + SetLength(LTitle, 512); + LLen := GetWindowTextW(LTarget, PChar(LTitle), 512); + SetLength(LTitle, LLen); + end; + FOnQuickSearchHotkey(akPasswordOnly, LTarget, LTitle); +end; + +procedure TPMBridge.ReloadLazyBar; +begin + PostMessage(FMsgWindow, WM_LAZY_RELOAD, 0, 0); +end; + procedure TPMBridge.RestoreLazyBar; begin if GetPref(LAZY_ON_KEY) = '1' then SetLazyBar(True); end; procedure TPMBridge.SetLazyBar(AOn: Boolean); +// Declarees AVANT les routines imbriquees, qui s'en servent : une routine +// imbriquee ne voit que les variables declarees au-dessus d'elle. +var + LWork, LRect: TRect; + LSaved: TArray; + LX, LY, LPosX, LPosY: Integer; + LList: string; + LTip: HWND; // BS_OWNERDRAW : le systeme garde le suivi de la souris et l'etat enfonce, // nous ne reprenons que les couleurs. Un bouton standard resterait gris clair // sur fond sombre, comme sur la capture qui a motive ce changement. - procedure MakeButton(AId: Integer; const ACaption: string; + procedure MakeButton(AId: Integer; const ACaption, ATip: string; AX, AWidth: Integer); var LBtn: HWND; + LTool: TToolInfo; begin LBtn := CreateWindow('BUTTON', PChar(ACaption), WS_CHILD or WS_VISIBLE or BS_OWNERDRAW, AX, 5, AWidth, 28, @@ -1349,12 +1471,32 @@ procedure TPMBridge.SetLazyBar(AOn: Boolean); SendMessage(LBtn, WM_SETFONT, WPARAM(GetStockObject(DEFAULT_GUI_FONT)), 1); GLazyOldBtnProc := Pointer(SetWindowLongPtr(LBtn, GWLP_WNDPROC, LONG_PTR(@LazyButtonProc))); + // UNE ICONE SANS MOT SE NOMME AU SURVOL. Info-bulle posee dans les deux + // modes : elle dit aussi ce que fait "Add" ou "Search" en texte. + if LTip <> 0 then + begin + FillChar(LTool, SizeOf(LTool), 0); + LTool.cbSize := SizeOf(LTool); + LTool.uFlags := TTF_IDISHWND or TTF_SUBCLASS; + LTool.hwnd := FLazyWnd; + LTool.uId := UINT_PTR(LBtn); + LTool.lpszText := PChar(ATip); + SendMessage(LTip, TTM_ADDTOOL, 0, LPARAM(@LTool)); + end; + end; + + // Un bouton du jeu choisi dans les reglages, pose a la suite des precedents. + procedure AddButton(const AKey: string; AId: Integer; + const ACaption, ATip: string; ATextWidth: Integer); + var + LW: Integer; + begin + if not (',' + LList + ',').Contains(',' + AKey + ',') then Exit; + LW := IfThen(FLazyIcons, 30, ATextWidth); + MakeButton(AId, ACaption, ATip, LX, LW); + Inc(LX, LW + 6); end; -var - LWork, LRect: TRect; - LSaved: TArray; - LX, LY, LPosX, LPosY: Integer; begin if AOn = LazyBarVisible then Exit; if AOn then SetPref(LAZY_ON_KEY, '1') else SetPref(LAZY_ON_KEY, '0'); @@ -1376,23 +1518,37 @@ begin if (FLazyAlphaPct < 25) or (FLazyAlphaPct > 100) then FLazyAlphaPct := LAZY_ALPHA_DEFAULT; - MakeButton(LAZY_ID_FULL, 'Full', 5, 56); - MakeButton(LAZY_ID_PWD, 'Password', 67, 76); - MakeButton(LAZY_ID_TOTP, '2FA', 149, 52); + FLazyIcons := GetPref(LAZY_ICONS_KEY) = '1'; + LList := GetPref(LAZY_BTNS_KEY); + if LList = '' then LList := LAZY_BTNS_DEFAULT; + InitCommonControls; + LTip := CreateWindowEx(WS_EX_TOPMOST, TOOLTIPS_CLASS, nil, + WS_POPUP or TTS_ALWAYSTIP or TTS_NOPREFIX, + Integer(CW_USEDEFAULT), Integer(CW_USEDEFAULT), + Integer(CW_USEDEFAULT), Integer(CW_USEDEFAULT), + FLazyWnd, 0, HInstance, nil); + + LX := 5; + AddButton('full', LAZY_ID_FULL, 'Full', 'Fill username and password', 56); + AddButton('pwd', LAZY_ID_PWD, 'Password', 'Fill the password only', 76); + AddButton('totp', LAZY_ID_TOTP, '2FA', 'Fill the 2FA code', 52); + AddButton('add', LAZY_ID_ADD, 'Add', 'New entry from the current window', 48); + AddButton('search', LAZY_ID_SEARCH, 'Search', 'Quick search', 60); // Meme raison que le X : 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. - MakeButton(LAZY_ID_OPEN, 'Open', 207, 56); + AddButton('open', LAZY_ID_OPEN, 'Open', 'Open the vault', 56); // Fermer la barrette sans passer par le menu du tray : elle flotte au-dessus // de tout, donc s'en debarrasser doit se faire la ou elle est. - MakeButton(LAZY_ID_CLOSE, 'X', 269, 24); + MakeButton(LAZY_ID_CLOSE, 'X', 'Close the fill bar', LX, 24); + FLazyW := LX + 24 + 5; // Bottom right of the WORK area, so it does not start life under the // taskbar. From there the user drags it wherever the login form is. if not SystemParametersInfo(SPI_GETWORKAREA, 0, @LWork, 0) then LWork := Rect(0, 0, GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN)); - LX := LWork.Right - LAZY_W - 24; + LX := LWork.Right - FLazyW - 24; LY := LWork.Bottom - LAZY_H - 24; // LA POSITION RETENUE, SI ELLE TOMBE ENCORE SUR UN ECRAN. Un ecran debranche @@ -1403,7 +1559,7 @@ begin if (Length(LSaved) = 2) and TryStrToInt(LSaved[0], LPosX) and TryStrToInt(LSaved[1], LPosY) then begin - LRect := Rect(LPosX, LPosY, LPosX + LAZY_W, LPosY + LAZY_H); + LRect := Rect(LPosX, LPosY, LPosX + FLazyW, LPosY + LAZY_H); if MonitorFromRect(@LRect, MONITOR_DEFAULTTONULL) <> 0 then begin LX := LPosX; @@ -1411,7 +1567,7 @@ begin end; end; - SetWindowPos(FLazyWnd, HWND_TOPMOST, LX, LY, LAZY_W, LAZY_H, + SetWindowPos(FLazyWnd, HWND_TOPMOST, LX, LY, FLazyW, LAZY_H, SWP_NOACTIVATE or SWP_SHOWWINDOW); // La souris est ailleurs a la seconde ou la barrette parait : elle nait donc @@ -1435,6 +1591,16 @@ begin Exit; end + else if AMsg.Msg = WM_LAZY_RELOAD then + begin + if LazyBarVisible then + begin + SetLazyBar(False); + SetLazyBar(True); + end; + Exit; + end + else if AMsg.Msg = WM_TRAY_ICON then begin // For Shell_NotifyIcon callback messages, the mouse event is in the @@ -1495,50 +1661,10 @@ begin end else if (AMsg.Msg = WM_HOTKEY) and (AMsg.WParam = NEW_ENTRY_HOTKEY_ID) then - begin - if Assigned(FOnNewEntryHotkey) then - begin - var LTarget := GetForegroundWindow; - var LTitle: string := ''; - // The window we are about to cover is exactly the one to hand the - // foreground back to when we return to the tray. - CaptureForegroundOwner; - // From the tray the foreground can resolve to OUR own window — don't - // pre-fill the new entry with "Password Manager". Only capture a real - // external window's title; otherwise pass '' (blank title). - if not IsOwnProcessWindow(LTarget) then - begin - SetLength(LTitle, 512); - var LLen := GetWindowTextW(LTarget, PChar(LTitle), 512); - SetLength(LTitle, LLen); - end; - FOnNewEntryHotkey(LTitle); - end; - end + FireNewEntryRequest else if (AMsg.Msg = WM_HOTKEY) and (AMsg.WParam = QUICK_SEARCH_HOTKEY_ID) then - begin - // Quick-search + autofill: capture the foreground HWND BEFORE the JS - // modal steals focus, hand it to the host so it can stash it in - // FAutofillTargetHWND (consumed by cmd://autofill/execute later). - if Assigned(FOnQuickSearchHotkey) then - begin - var LTarget := GetForegroundWindow; - CaptureForegroundOwner; - // Same trap as the autofill hotkeys: our own hidden window can be the - // foreground one. Report no target — the host then opens quick search in - // copy mode instead of offering to type into ourselves. - if IsOwnProcessWindow(LTarget) then LTarget := 0; - var LTitle: string; - if LTarget <> 0 then - begin - SetLength(LTitle, 512); - var LLen := GetWindowTextW(LTarget, PChar(LTitle), 512); - SetLength(LTitle, LLen); - end; - FOnQuickSearchHotkey(akPasswordOnly, LTarget, LTitle); - end; - end + FireQuickSearchRequest else if (AMsg.Msg = WM_HOTKEY) and Assigned(FOnAutofillRequest) and ((AMsg.WParam = AUTOFILL_HOTKEY_ID_FULL) or diff --git a/delphi-backend/UMainForm.pas b/delphi-backend/UMainForm.pas index 9d44fed..4b2758a 100644 --- a/delphi-backend/UMainForm.pas +++ b/delphi-backend/UMainForm.pas @@ -1323,6 +1323,42 @@ begin 'Bridge.onPrefResult("' + LEsc + '#has","' + LSet + '")'); end + // ---- Barrette (Fill bar) : reglages depuis Settings -> Fill bar --------- + // Rien de secret ici : quels boutons, icones ou mots, opacite, allumee. + else if ACmd = 'fillbar/get' then + begin + var LFb := TJSONObject.Create; + try + LFb.AddPair('on', TJSONBool.Create(FBridge.LazyBarVisible)); + LFb.AddPair('buttons', PM.UserPrefs.GetPref('lazyButtons')); + LFb.AddPair('icons', TJSONBool.Create(PM.UserPrefs.GetPref('lazyIcons') = '1')); + LFb.AddPair('alpha', PM.UserPrefs.GetPref('lazyAlphaPct')); + WebBrowser.ExecuteJavaScript( + 'if(window.Bridge&&Bridge.onFillBar)Bridge.onFillBar(' + LFb.ToJSON + ')'); + finally + LFb.Free; + end; + end + + else if ACmd = 'fillbar/set' then + begin + PM.UserPrefs.SetPref('lazyButtons', GetParam('buttons')); + if GetParam('icons') = '1' then PM.UserPrefs.SetPref('lazyIcons', '1') + else PM.UserPrefs.SetPref('lazyIcons', '0'); + var LAlpha := StrToIntDef(GetParam('alpha'), 60); + if (LAlpha >= 25) and (LAlpha <= 100) then + PM.UserPrefs.SetPref('lazyAlphaPct', IntToStr(LAlpha)); + LogLine('fillbar/set: ' + GetParam('buttons') + ' icons=' + GetParam('icons') + + ' alpha=' + GetParam('alpha') + ' on=' + GetParam('on')); + if GetParam('on') = '1' then + begin + if FBridge.LazyBarVisible then FBridge.ReloadLazyBar + else FBridge.SetLazyBar(True); + end + else + FBridge.SetLazyBar(False); + end + else if ACmd = 'prefs/set' then begin var LKey := GetParam('key'); diff --git a/index.html b/index.html index 21d4e34..1162ee1 100644 --- a/index.html +++ b/index.html @@ -444,6 +444,7 @@ + @@ -738,6 +739,53 @@ +
+
Fill bar
+

+ A small floating bar that does with the mouse what the hotkeys + do from the keyboard. Right-click it for the same opacity and + icons/text choices. +

+
+ Show the fill bar + +
+
+ Buttons + + + + + + + + +
+
+ Style + +
+
+ + Opacity when the mouse leaves + Full again as soon as the mouse is over it. + + +
+
+
Clipboard privacy

diff --git a/js/app.js b/js/app.js index cfedde7..978da08 100644 --- a/js/app.js +++ b/js/app.js @@ -6696,7 +6696,33 @@ function revealSyncSettings() { }, 0); } +// BARRETTE (Fill bar). Ses reglages vivent en prefs DPAPI cote Delphi, pas dans +// settings_json : elle s'allume coffre ferme, sans session. La page ne fait que +// les lire (fillbar/get) et les renvoyer tous ensemble a chaque changement. +function fillBarRequest() { + if (Bridge.active) window.location.href = 'cmd://fillbar/get'; +} +Bridge.onFillBar = function (st) { + const keys = (st.buttons || 'full,pwd,totp,add,search,open').split(','); + $('#fbOn').checked = !!st.on; + $$('.fb-btn').forEach(c => { c.checked = keys.includes(c.dataset.key); }); + $('#fbStyle').value = st.icons ? 'icons' : 'text'; + $('#fbAlpha').value = String(parseInt(st.alpha, 10) || 60); +}; +function fillBarPush() { + const keys = Array.from($$('.fb-btn')).filter(c => c.checked).map(c => c.dataset.key); + window.location.href = 'cmd://fillbar/set' + + '?on=' + ($('#fbOn').checked ? '1' : '0') + + '&buttons=' + encodeURIComponent(keys.join(',') || 'none') + + '&icons=' + ($('#fbStyle').value === 'icons' ? '1' : '0') + + '&alpha=' + encodeURIComponent($('#fbAlpha').value); +} + function openSettings() { + // Hors de l'exe (navigateur de dev) il n'y a pas de barrette. + $('#fillbarSection').style.display = Bridge.active ? '' : 'none'; + $('#settingsTabFillbar').style.display = Bridge.active ? '' : 'none'; + fillBarRequest(); $('#settingTheme').value = state.theme; $('#settingSort').value = state.sortBy + ':' + state.sortDir; $('#settingAutoLock').value = String(state.autoLock); @@ -6819,6 +6845,7 @@ const SETTINGS_TAB_OF = { 'Appearance': 'general', 'Security': 'security', 'Clipboard privacy': 'security', 'Autofill': 'autofill', + 'Fill bar': 'fillbar', 'Account': 'account', 'PIN unlock': 'account', 'Quick unlock': 'account', 'Recovery key': 'account', 'Export': 'sync', 'Sync (WebDAV or Gitea)': 'sync', @@ -8193,6 +8220,11 @@ async function init() { if (syncTest) syncTest.addEventListener('click', syncTestConnection); const syncQr = document.getElementById('syncQrBtn'); if (syncQr) syncQr.addEventListener('click', syncQrToggle); + ['#fbOn', '#fbStyle', '#fbAlpha'].forEach(sel => { + const el = $(sel); + if (el) el.addEventListener('change', fillBarPush); + }); + $$('.fb-btn').forEach(c => c.addEventListener('change', fillBarPush)); // Guard the user entry point: ignore clicks while a sync is running (no // concurrent syncs) and call runSyncNow() with NO arg — the click event // must not land in its `_attempt` retry counter. Internal retries call