diff --git a/CLAUDE.md b/CLAUDE.md
index 6fc5b52..a2ba0e9 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -316,6 +316,16 @@ Détails :
Le cadre est peint dans `WM_ERASEBKGND` plutôt que par `WS_BORDER`, qui se
sert dans les couleurs système.
+**Bouton Paste (2026-10-14)** : `LAZY_ID_PASTE` / cle `paste` (desactive par
+defaut) → `PasteClipboardAsKeystrokes` lit le presse-papiers **Windows**
+(`CF_UNICODETEXT`) et le **tape** dans la fenetre en avant par
+`ExecuteAutofill(..., AClearFirst=False)` - donc vraies touches et bascule de
+disposition vers un client VNC (TigerVNC vers un Mac ne transmet pas le
+presse-papiers). Insere au curseur, saut de ligne final retire (un terminal
+lancerait la commande), sauts internes tapes en Entree. Marche coffre ferme :
+rien ne passe par la page. Pas de texte (image, fichier) → bulle « No text in
+the clipboard ».
+
### Réglages de la barrette (2026-09-18)
**Settings → onglet Fill bar**, et le **clic droit** sur la barrette pour les
@@ -1612,6 +1622,10 @@ après chaque `DELETE /attachments/{id}`. Un petit vault sain ne paie rien.
texte = fin de la commande ; un `#` apres une continuation reste du texte.
Limite : un chemin Windows fini par `\` en fin de ligne passe pour une
continuation.
+- **Polices a chasse fixe et arabe** (2026-10-14) : aucune n'a l'arabe, et
+ Windows empruntait une police plus petite. Chaque pile `…, monospace` de
+ `css/style.css` finit par `'Segoe UI', Tahoma, monospace` : le latin reste a
+ chasse fixe, l'arabe prend une taille comparable.
- **Settings search** : `#settingsSearch` filtre les `.setting-row` par
texte ; match sur le label de section garde toute la section. Esc dans
la search avec query = clear ; Esc vide = ferme Settings.
diff --git a/css/style.css b/css/style.css
index c2debbd..0debf7a 100644
--- a/css/style.css
+++ b/css/style.css
@@ -17,10 +17,14 @@ body {
overflow: hidden;
}
+/* Les polices a chasse fixe n'ont pas l'arabe : Windows empruntait une police
+ au hasard, plus petite. 'Segoe UI' / Tahoma, places avant 'monospace' dans
+ chaque pile, prennent ces caracteres a une taille comparable ; le latin reste
+ dans la police a chasse fixe. */
button { font-family: inherit; cursor: pointer; }
input, select, textarea { font-family: inherit; font-size: inherit; }
kbd {
- font-family: 'JetBrains Mono', 'SF Mono', Consolas, monospace;
+ font-family: 'JetBrains Mono', 'SF Mono', Consolas, 'Segoe UI', Tahoma, monospace;
font-size: 11px;
padding: 2px 6px;
border: 1px solid var(--border);
@@ -248,7 +252,7 @@ input[type="range"]::-webkit-slider-thumb {
.licence-machine code {
display: inline-block; padding: 6px 10px;
background: var(--bg); border: 1px solid var(--border); border-radius: var(--radius-sm);
- font-family: ui-monospace, "Cascadia Mono", Consolas, monospace;
+ font-family: ui-monospace, "Cascadia Mono", Consolas, 'Segoe UI', Tahoma, monospace;
font-size: 14px; letter-spacing: 1px; color: var(--text);
user-select: all;
}
@@ -1216,7 +1220,7 @@ input[type="range"]::-webkit-slider-thumb {
}
.entry-pw {
flex: 1;
- font-family: 'JetBrains Mono', 'SF Mono', Consolas, monospace;
+ font-family: 'JetBrains Mono', 'SF Mono', Consolas, 'Segoe UI', Tahoma, monospace;
font-size: 12px;
color: var(--text-dim);
overflow: hidden;
@@ -1268,7 +1272,7 @@ input[type="range"]::-webkit-slider-thumb {
}
.totp-code {
flex: 1;
- font-family: 'JetBrains Mono', ui-monospace, monospace;
+ font-family: 'JetBrains Mono', ui-monospace, 'Segoe UI', Tahoma, monospace;
font-size: 22px;
letter-spacing: 2px;
color: var(--accent);
@@ -1330,7 +1334,7 @@ input[type="range"]::-webkit-slider-thumb {
}
.auth-card-code {
flex: 1;
- font-family: 'JetBrains Mono', ui-monospace, monospace;
+ font-family: 'JetBrains Mono', ui-monospace, 'Segoe UI', Tahoma, monospace;
font-size: 26px;
letter-spacing: 3px;
color: var(--accent);
@@ -1386,7 +1390,7 @@ input[type="range"]::-webkit-slider-thumb {
.health-score.is-danger { border-color: #ef4444; color: #ef4444; }
.health-score-num {
font-size: 36px; font-weight: 700;
- font-family: 'JetBrains Mono', ui-monospace, monospace;
+ font-family: 'JetBrains Mono', ui-monospace, 'Segoe UI', Tahoma, monospace;
}
.health-score-lbl {
font-size: 11px; font-weight: 600;
@@ -1417,7 +1421,7 @@ input[type="range"]::-webkit-slider-thumb {
.health-bd-n { text-align: right; font-variant-numeric: tabular-nums; }
.health-bd-pts {
text-align: right;
- font-family: 'JetBrains Mono', ui-monospace, monospace;
+ font-family: 'JetBrains Mono', ui-monospace, 'Segoe UI', Tahoma, monospace;
color: var(--text-faint);
}
/* Nothing wrong in this category — dim it so the eye goes to what needs work. */
@@ -1572,7 +1576,7 @@ input[type="range"]::-webkit-slider-thumb {
.history-date {
font-size: 11px;
color: var(--text-dim);
- font-family: 'JetBrains Mono', ui-monospace, monospace;
+ font-family: 'JetBrains Mono', ui-monospace, 'Segoe UI', Tahoma, monospace;
}
.history-preview {
display: flex; align-items: flex-start; gap: 6px;
@@ -1581,7 +1585,7 @@ input[type="range"]::-webkit-slider-thumb {
.history-value {
flex: 1 1 0;
min-width: 0;
- font-family: 'JetBrains Mono', ui-monospace, monospace;
+ font-family: 'JetBrains Mono', ui-monospace, 'Segoe UI', Tahoma, monospace;
font-size: 12px;
color: var(--text);
/* Long revealed passwords wrap to multiple lines (no ellipsis) so
@@ -1634,7 +1638,7 @@ input[type="range"]::-webkit-slider-thumb {
flex-wrap: wrap;
}
.cheatsheet-keys kbd {
- font-family: 'JetBrains Mono', ui-monospace, monospace;
+ font-family: 'JetBrains Mono', ui-monospace, 'Segoe UI', Tahoma, monospace;
font-size: 11px;
padding: 2px 7px;
background: var(--bg);
@@ -1814,7 +1818,7 @@ input[type="range"]::-webkit-slider-thumb {
.audit-row:hover { background: var(--bg); }
.audit-date {
color: var(--text-dim);
- font-family: 'JetBrains Mono', ui-monospace, monospace;
+ font-family: 'JetBrains Mono', ui-monospace, 'Segoe UI', Tahoma, monospace;
white-space: nowrap;
}
.audit-action {
@@ -1823,7 +1827,7 @@ input[type="range"]::-webkit-slider-thumb {
}
.audit-ip {
color: var(--text-faint);
- font-family: 'JetBrains Mono', ui-monospace, monospace;
+ font-family: 'JetBrains Mono', ui-monospace, 'Segoe UI', Tahoma, monospace;
text-align: right;
}
@@ -1969,7 +1973,7 @@ input[type="range"]::-webkit-slider-thumb {
.so-note-body {
min-height: 220px;
resize: vertical;
- font-family: 'JetBrains Mono', ui-monospace, monospace;
+ font-family: 'JetBrains Mono', ui-monospace, 'Segoe UI', Tahoma, monospace;
font-size: 13px;
line-height: 1.55;
white-space: pre-wrap;
@@ -1987,7 +1991,7 @@ input[type="range"]::-webkit-slider-thumb {
.so-cmds-cmd { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 12px; }
.so-cmds-empty { font-size: 12px; color: var(--text-dim); }
#qsBack { flex-shrink: 0; }
-.qs-cmd { font-family: ui-monospace, Consolas, monospace; font-size: 13px; }
+.qs-cmd { font-family: ui-monospace, Consolas, 'Segoe UI', Tahoma, monospace; font-size: 13px; }
/* ---- + New dropdown ---------------------------------- */
.new-entry-wrap {
@@ -2543,7 +2547,7 @@ body[data-editor-position="center"]:has(#settingsPanel.is-open)::before {
background: var(--bg);
border: 1px solid var(--border);
border-radius: var(--radius-sm);
- font-family: 'JetBrains Mono', monospace;
+ font-family: 'JetBrains Mono', 'Segoe UI', Tahoma, monospace;
font-size: 13px;
word-break: break-all;
margin-bottom: 16px;
@@ -2780,7 +2784,7 @@ body[data-editor-position="center"]:has(#settingsPanel.is-open)::before {
/* ---- Hotkey capture buttons (Settings → Autofill) ---- */
.hotkey-capture-btn {
- font-family: ui-monospace, "Cascadia Code", Menlo, monospace;
+ font-family: ui-monospace, "Cascadia Code", Menlo, 'Segoe UI', Tahoma, monospace;
font-size: 11px;
min-width: 140px;
text-align: center;
@@ -3240,7 +3244,7 @@ input[type="password"]::-ms-clear {
margin-bottom: 28px;
}
.prc-code {
- font-family: 'Courier New', monospace;
+ font-family: 'Courier New', 'Segoe UI', Tahoma, monospace;
font-size: 32px;
letter-spacing: 6px;
text-align: center;
diff --git a/delphi-backend/Source/PM.Bridge.pas b/delphi-backend/Source/PM.Bridge.pas
index 7ab1ad8..fb2a5db 100644
--- a/delphi-backend/Source/PM.Bridge.pas
+++ b/delphi-backend/Source/PM.Bridge.pas
@@ -183,6 +183,7 @@ type
// les declenche aussi, meme capture de la fenetre cible.
procedure FireNewEntryRequest;
procedure FireQuickSearchRequest;
+ procedure PasteClipboardAsKeystrokes;
/// 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.
@@ -450,6 +451,7 @@ const
LAZY_ID_ADD = 106; // nouvelle entree depuis la fenetre en avant (Ctrl+Shift+A)
LAZY_ID_SEARCH = 107; // recherche rapide (Ctrl+Shift+Q)
LAZY_ID_SETTINGS = 108; // reglages, onglet Fill bar
+ LAZY_ID_PASTE = 109; // tape le presse-papiers Windows dans la fenetre en avant
LAZY_MENU_ICONS = 1; // hors des valeurs d'opacite (25..100) du meme menu
LAZY_MENU_SETTINGS = 2;
LAZY_MENU_CLEAR = 3;
@@ -1116,6 +1118,59 @@ end;
// The target of a fill is whatever window owns the foreground at the moment
// the user asks for it - a shortcut, or a click on the lazy strip, which takes
// no foreground precisely so this stays true.
+/// COLLER EN TAPANT. Certains clients a distance (TigerVNC vers un Mac) ne
+/// transmettent pas le presse-papiers Windows : Ctrl+V n'y colle rien. Le texte
+/// est donc TAPE dans la fenetre en avant, par le meme chemin que l'autofill -
+/// vraies touches pour un client VNC, dispositions basculees au besoin. Le
+/// presse-papiers est celui de Windows : coffre ouvert ou non, rien ne passe
+/// par la page. Pas de Ctrl+A + Suppr avant : on insere au curseur, comme un
+/// collage.
+procedure TPMBridge.PasteClipboardAsKeystrokes;
+var
+ LTarget: HWND;
+ LData: THandle;
+ LPtr: PWideChar;
+ LText: string;
+begin
+ LTarget := GetForegroundWindow;
+ if IsOwnProcessWindow(LTarget) then
+ begin
+ ShowBalloon('Nothing to paste into',
+ 'Click where the text should go first, then press Paste.', True);
+ Exit;
+ end;
+ LText := '';
+ if OpenClipboard(0) then
+ try
+ LData := GetClipboardData(CF_UNICODETEXT);
+ if LData <> 0 then
+ begin
+ LPtr := GlobalLock(LData);
+ if LPtr <> nil then
+ try
+ LText := LPtr;
+ finally
+ GlobalUnlock(LData);
+ end;
+ end;
+ finally
+ CloseClipboard;
+ end;
+ // Un retour a la ligne final, tape dans un terminal, vaudrait Entree.
+ // Les sauts de ligne internes partent en #13 : Entree, comme un collage.
+ LText := LText.Replace(#13#10, #10).Replace(#13, #10).TrimRight([#10])
+ .Replace(#10, #13);
+ if LText = '' then
+ begin
+ // Une image ou un fichier copies ne sont pas du texte : le dire tel quel.
+ ShowBalloon('No text in the clipboard', 'Copy some text first, then press Paste.', True);
+ Exit;
+ end;
+ if not ExecuteAutofill(LTarget, '', LText, False, False, False) then
+ ShowBalloon('Paste blocked',
+ 'The window did not accept the keystrokes (it may run as administrator).', True);
+end;
+
procedure TPMBridge.FireAutofillRequest(AKind: TAutofillKind;
AFromBar: Boolean = False);
var
@@ -1302,6 +1357,7 @@ begin
LAZY_ID_SEARCH: LGlyph := #$E721; // Search
LAZY_ID_OPEN: LGlyph := #$E8A7; // OpenInNewWindow
LAZY_ID_SETTINGS: LGlyph := #$E713; // Settings (roue)
+ LAZY_ID_PASTE: LGlyph := #$E77F; // Paste
else
LGlyph := #$E711; // Cancel (X)
end;
@@ -1505,6 +1561,7 @@ begin
LAZY_ID_OPEN: if Assigned(FOnTrayRestore) then FOnTrayRestore();
LAZY_ID_ADD: FireNewEntryRequest;
LAZY_ID_SEARCH: FireQuickSearchRequest;
+ LAZY_ID_PASTE: PasteClipboardAsKeystrokes;
LAZY_ID_SETTINGS:
begin
if Assigned(FOnTrayRestore) then FOnTrayRestore();
@@ -1669,6 +1726,7 @@ begin
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('paste', LAZY_ID_PASTE, 'Paste', 'Type the Windows clipboard into the current window', 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
diff --git a/index.html b/index.html
index 1afbd8c..a3a234f 100644
--- a/index.html
+++ b/index.html
@@ -768,6 +768,7 @@
+