Skip to content

Commit f87affa

Browse files
feat(quick): mask Quick-Upload code with show/hide toggle
The /q code input is now a password field by default so the code isn't readable over someone's shoulder in a public place, with an Eye/EyeOff button to reveal it on demand. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WNvAeoRFGJkRnaBGwMstZ9
1 parent 28350f3 commit f87affa

2 files changed

Lines changed: 24 additions & 8 deletions

File tree

apps/web/src/app/q/page.tsx

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
import { useEffect, useRef, useState } from 'react';
44
import Link from 'next/link';
5-
import { UploadCloud, CheckCircle2 } from 'lucide-react';
5+
import { UploadCloud, CheckCircle2, Eye, EyeOff } from 'lucide-react';
66
import { API_URL } from '@/lib/api';
77
import { useT } from '@/lib/i18n';
88
import { Logo } from '@/components/Logo';
@@ -16,6 +16,7 @@ import { Wordmark } from '@/components/Wordmark';
1616
export default function QuickUploadPage() {
1717
const { t } = useT();
1818
const [code, setCode] = useState('');
19+
const [showCode, setShowCode] = useState(false);
1920
const [password, setPassword] = useState('');
2021
const [requiresPassword, setRequiresPassword] = useState(false);
2122
const [validated, setValidated] = useState(false);
@@ -93,13 +94,26 @@ export default function QuickUploadPage() {
9394
<div className="card space-y-4">
9495
{!validated ? (
9596
<form onSubmit={check} className="space-y-3">
96-
<input
97-
className="input text-center font-mono text-lg uppercase tracking-[0.3em]"
98-
placeholder={t('quick.codePlaceholder')}
99-
value={code}
100-
onChange={(e) => setCode(e.target.value.toUpperCase())}
101-
required
102-
/>
97+
<div className="relative">
98+
<input
99+
className="input text-center font-mono text-lg uppercase tracking-[0.3em]"
100+
type={showCode ? 'text' : 'password'}
101+
autoComplete="off"
102+
placeholder={t('quick.codePlaceholder')}
103+
value={code}
104+
onChange={(e) => setCode(e.target.value.toUpperCase())}
105+
required
106+
/>
107+
<button
108+
type="button"
109+
tabIndex={-1}
110+
onClick={() => setShowCode((s) => !s)}
111+
aria-label={t('quick.toggleCode')}
112+
className="absolute inset-y-0 right-2 grid place-items-center text-zinc-500 transition hover:text-zinc-300"
113+
>
114+
{showCode ? <EyeOff size={16} /> : <Eye size={16} />}
115+
</button>
116+
</div>
103117
<button className="btn-primary w-full">{t('quick.continue')}</button>
104118
</form>
105119
) : (

apps/web/src/lib/i18n.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -453,6 +453,7 @@ const fr: Dict = {
453453
'quick.title': 'Quick-Upload',
454454
'quick.subtitle': 'Entrez un code actif pour ouvrir une zone de dépôt temporaire.',
455455
'quick.codePlaceholder': 'CODE',
456+
'quick.toggleCode': 'Afficher / masquer le code',
456457
'quick.continue': 'Continuer',
457458
'quick.areaPassword': 'Mot de passe de la zone',
458459
'quick.uploading': 'Envoi…',
@@ -958,6 +959,7 @@ const en: Dict = {
958959
'quick.title': 'Quick-Upload',
959960
'quick.subtitle': 'Enter an active code to open a temporary drop zone.',
960961
'quick.codePlaceholder': 'CODE',
962+
'quick.toggleCode': 'Show / hide the code',
961963
'quick.continue': 'Continue',
962964
'quick.areaPassword': 'Drop zone password',
963965
'quick.uploading': 'Uploading…',

0 commit comments

Comments
 (0)