fix(idle): put the fingerprint hint where it can actually be seen
All checks were successful
Check / eval (push) Successful in 3m0s

e2de906 fixed the invisible reader and left an invisible message. The lock
screen is deliberately just the clock: the input field fades out while empty
(hyprlock's fade_on_empty default) and only appears once you type. So
$FPRINTPROMPT in the field's placeholder rendered faithfully into a widget
nobody sees until they have already given up on the finger and started typing
— which Bernardo reported as "unlock worked, but still no message", the same
symptom as before the fix, for a completely different reason.

I had assumed the field was visible and reasoned from there. It has never been
visible; "it has always been like this" is what cracked it.

Both candidate surfaces were tested on hardware rather than picked: a label
does expand $FPRINTPROMPT (so the line is live — "password… or scan finger",
then "scanning finger…" on touch), and fade_on_empty=false would also work.
Bernardo chose the label: it keeps the clock-only look, where the always-on
field would have made opting into fingerprint mean permanently gaining a
widget the design never had.

subtext-on-base is the palette's secondary-text role and is already held to a
3.0 floor on all 21 themes by checks.theme-contrast — the guard that exists
because two themes once shipped subtext == base and made hint text invisible,
which is the same bug as this commit, one layer down. The placeholder goes
back to its exact previous string, so the non-fingerprint path is unchanged.

Verified V2: nix flake check --no-build; checks.theme-contrast and
checks.option-docs pass. V3: Bernardo confirms the line reads on the real
lock screen — the mechanism itself is already proven on his reader.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-14 13:05:50 +01:00
parent e2de9062d8
commit fbbeac6c79

View File

@@ -43,17 +43,19 @@ in
check_color = rgb c.warn; check_color = rgb c.warn;
fail_color = rgb c.bad; fail_color = rgb c.bad;
rounding = t.ui.rounding; rounding = t.ui.rounding;
# $FPRINTPROMPT is the ONLY place hyprlock renders the fingerprint placeholder_text = "<i>password</i>";
# ready/scanning/failed messages — there is no implicit slot for
# them. Enabling the backend without this yields a reader that
# silently works while the field still says "password…", i.e. a
# feature nobody can find (VISION: fingerprint discoverable without
# reading the README). Verified on hardware 2026-07-14: unlock by
# finger worked and said nothing at all.
placeholder_text =
if cfg.idle.fingerprint then "<i>$FPRINTPROMPT</i>" else "<i>password</i>";
}]; }];
# The lock screen is deliberately just the clock: the input field
# fades out while empty (hyprlock's fade_on_empty default) and only
# appears once you type. So the fingerprint hint CANNOT live in the
# field's placeholder — $FPRINTPROMPT renders faithfully into a widget
# nobody sees until they have already given up on the reader and
# started typing. It needs a surface that is visible at rest, and a
# label is the one that keeps the clock-only look. (Confirmed on
# hardware 2026-07-14: labels do expand $FPRINTPROMPT, so the line is
# live — "password… or scan finger", then "scanning finger…" on touch —
# and not static text.)
label = [{ label = [{
monitor = ""; monitor = "";
text = "$TIME"; text = "$TIME";
@@ -63,7 +65,20 @@ in
position = "0, 120"; position = "0, 120";
halign = "center"; halign = "center";
valign = "center"; valign = "center";
}]; }] ++ lib.optional cfg.idle.fingerprint {
monitor = "";
text = "$FPRINTPROMPT";
# subtext-on-base is the palette's secondary-text role and is held
# to a 3.0 contrast floor on every theme by checks.theme-contrast —
# the same guard that exists because two themes once shipped
# subtext == base and made hint text invisible.
color = rgb c.subtext;
font_size = 16;
font_family = t.fonts.ui;
position = "0, -160";
halign = "center";
valign = "center";
};
} // lib.optionalAttrs cfg.idle.fingerprint { } // lib.optionalAttrs cfg.idle.fingerprint {
# hyprlock does NOT take a fingerprint through PAM: its PAM stack runs # hyprlock does NOT take a fingerprint through PAM: its PAM stack runs
# only on submit, so a parallel module never gets to poll the reader. # only on submit, so a parallel module never gets to poll the reader.