@@ -13,7 +13,11 @@ import BottomSheet from "$lib/ui/BottomSheet/BottomSheet.svelte";
1313import LoadingSheet from " $lib/ui/LoadingSheet/LoadingSheet.svelte" ;
1414import { capitalize } from " $lib/utils" ;
1515import { PERSONAL_BINDING_BY_TYPE_QUERY } from " $lib/utils/personalBinding" ;
16- import { ArrowLeft01Icon } from " @hugeicons/core-free-icons" ;
16+ import {
17+ ArrowLeft01Icon ,
18+ ViewIcon ,
19+ ViewOffIcon ,
20+ } from " @hugeicons/core-free-icons" ;
1721import { HugeiconsIcon } from " @hugeicons/svelte" ;
1822import {
1923 Format ,
@@ -124,6 +128,9 @@ let enameLoading = $state(false);
124128let answerInput = $state (" " );
125129let answerError = $state <string | null >(null );
126130let answerLoading = $state (false );
131+ // The security answer is sensitive — mask it by default, with an eye toggle
132+ // to reveal (spelling matters, so the user needs to be able to check it).
133+ let showAnswer = $state (false );
127134
128135// Question text + envelope id are pulled from the target eVault during
129136// handleSubmitEname. The envelope id is the metaEnvelopeId of the
@@ -554,6 +561,7 @@ async function handleSubmitEname() {
554561 answerInput = " " ;
555562 answerError = null ;
556563 lockedUntilLabel = null ;
564+ showAnswer = false ;
557565 step = " unverified-answer" ;
558566 } catch (err : unknown ) {
559567 console .error (" [RECOVERY/unverified] eName lookup error:" , err );
@@ -1337,17 +1345,35 @@ onMount(() => {
13371345 >
13381346 {securityQuestion } <span class =" text-danger" >*</span >
13391347 </label >
1340- <input
1341- id =" recover-answer"
1342- type =" text"
1343- bind:value ={answerInput }
1344- autocomplete =" off"
1345- autocapitalize =" off"
1346- autocorrect =" off"
1347- spellcheck =" false"
1348- placeholder =" Your answer"
1349- class =" w-full bg-card-alternative rounded-full px-5 py-4 placeholder:text-black-300 outline-none focus:ring-2 focus:ring-primary"
1350- />
1348+ <div class =" relative" >
1349+ <input
1350+ id =" recover-answer"
1351+ type ={showAnswer ? " text" : " password" }
1352+ bind:value ={answerInput }
1353+ autocomplete =" off"
1354+ autocapitalize =" off"
1355+ autocorrect =" off"
1356+ spellcheck =" false"
1357+ placeholder =" Your answer"
1358+ class =" w-full bg-card-alternative rounded-full pl-5 pr-14 py-4 placeholder:text-black-300 outline-none focus:ring-2 focus:ring-primary"
1359+ />
1360+ <button
1361+ type =" button"
1362+ onclick ={() => {
1363+ showAnswer = ! showAnswer ;
1364+ }}
1365+ aria-label ={showAnswer ? " Hide answer" : " Show answer" }
1366+ aria-pressed ={showAnswer }
1367+ class =" absolute inset-y-0 right-0 flex items-center pr-5 text-black-500 active:opacity-70"
1368+ >
1369+ <HugeiconsIcon
1370+ icon ={showAnswer ? ViewOffIcon : ViewIcon }
1371+ size ={20 }
1372+ color =" currentColor"
1373+ strokeWidth ={2 }
1374+ />
1375+ </button >
1376+ </div >
13511377 {#if answerError }
13521378 <p class =" text-danger text-sm font-medium" role =" alert" >
13531379 {answerError }
0 commit comments