mirror of
https://github.com/kccleoc/seedpgp-web.git
synced 2026-03-07 09:57:50 +08:00
feat: mobile-first redesign and layout improvements
## Major Changes ### Mobile-First Responsive Design - Converted entire app to mobile-first single-column layout - Constrained max-width to 448px (mobile phone width) - Black margins on desktop, centered content - Removed all multi-column grids (md:grid-cols-3) ### Header Reorganization (3-Row Layout) - Row 1: App logo + title + version - Row 2: Security badges + action buttons (Empty, Reset) - Row 3: Navigation tabs (Create, Backup, Restore, Blender) - Replaced text buttons with emoji icons (📋 clipboard, 🙈 privacy mask) - Consistent button sizing across all tabs ### Font Size Reductions - Reduced all button text sizes for mobile density - Main buttons: py-4 → py-3, added text-sm - Labels: text-xs → text-[10px] - Placeholders: consistent text-[10px] across all inputs - Input fields: text-sm → text-xs, p-4 → p-3 ### Create Tab Improvements - Changed "GENERATE NEW SEED" from button-style to banner - Left-aligned banner with gradient background - Equal-width button grid (12/24 Words, Backup/Seed Blender) - Used grid-cols-2 for consistent sizing ### Backup Tab Improvements - Simplified drag-drop area with 📎 emoji - Reduced padding and text sizes - Cleaner, shorter copy - PGP label font size: text-xs → text-[12px] ### SeedBlender Component - Reorganized mnemonic input cards: textarea on row 1, buttons on row 2 - QR button (left) and X button (right) alignment - Consistent placeholder text sizing (text-[10px]) - Shortened dice roll placeholder text ### HTTPS Development Server - Added @vitejs/plugin-basic-ssl for HTTPS in dev mode - Configured server to listen on 0.0.0.0:5173 - Fixed Web Crypto API issues on mobile (requires secure context) - Enables testing on iPhone via local network ## Technical Details - All changes maintain cyberpunk theme and color scheme - Improved mobile usability and visual consistency - No functionality changes, pure UI/UX improvements
This commit is contained in:
@@ -319,17 +319,32 @@ export function SeedBlender({ onDirtyStateChange, setMnemonicForBackup, requestT
|
||||
{entry.error && <p className="text-xs text-[#ff006e]">{entry.error}</p>}
|
||||
</div>
|
||||
) : (
|
||||
<div className="space-y-1">
|
||||
<div className="flex flex-col sm:flex-row items-start gap-2">
|
||||
<div className="relative w-full">
|
||||
<textarea value={entry.rawInput} onChange={(e) => updateEntry(index, { rawInput: e.target.value, decryptedMnemonic: e.target.value, isValid: null, error: null })} placeholder={`Mnemonic #${index + 1} (12 or 24 words)`} className={`w-full h-28 sm:h-24 p-3 pr-10 bg-[#16213e] border-2 rounded-lg text-sm font-mono text-[#00f0ff] placeholder-[#9d84b7] ${getBorderColor(entry.isValid)}`} />
|
||||
{entry.isValid === true && <CheckCircle2 className="absolute top-3 right-3 text-[#39ff14]" />}
|
||||
{entry.isValid === false && <AlertTriangle className="absolute top-3 right-3 text-[#ff006e]" />}
|
||||
</div>
|
||||
<div className="flex items-center gap-2 shrink-0">
|
||||
<button onClick={() => handleScan(index)} className="p-3 h-full bg-[#ff006e]/20 text-[#ff006e] hover:bg-[#ff006e]/50 hover:text-white rounded-md border-2 border-[#ff006e]/30"><QrCode size={20} /></button>
|
||||
<button onClick={() => handleRemoveEntry(entry.id)} className="p-3 h-full bg-[#ff006e]/20 text-[#ff006e] hover:bg-[#ff006e]/50 hover:text-white rounded-md border-2 border-[#ff006e]/30"><X size={20} /></button>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
{/* Row 1: Textarea only */}
|
||||
<textarea
|
||||
value={entry.rawInput}
|
||||
onChange={(e) => updateEntry(index, { rawInput: e.target.value, decryptedMnemonic: e.target.value, isValid: null, error: null })}
|
||||
placeholder={`Mnemonic #${index + 1} (12 or 24 words)`}
|
||||
className={`w-full h-24 p-3 bg-[#0a0a0f] border-2 rounded-lg font-mono text-xs placeholder:text-[10px] placeholder:text-[#6ef3f7] focus:outline-none focus:border-[#ff006e] focus:shadow-[0_0_20px_rgba(255,0,110,0.5)] transition-all ${getBorderColor(entry.isValid)}`}
|
||||
/>
|
||||
{/* Row 2: QR button (left) and X button (right) */}
|
||||
<div className="flex items-center justify-between">
|
||||
<button
|
||||
onClick={() => handleScan(index)}
|
||||
className="flex items-center gap-1.5 px-3 py-1.5 bg-[#16213e] border border-[#00f0ff] text-[#00f0ff] text-xs rounded-lg hover:bg-[#00f0ff20] transition-all"
|
||||
title="Scan QR code"
|
||||
>
|
||||
<QrCode size={14} />
|
||||
<span>Scan QR</span>
|
||||
</button>
|
||||
|
||||
<button
|
||||
onClick={() => handleRemoveEntry(entry.id)}
|
||||
className="p-1.5 bg-[#16213e] border border-[#ff006e] text-[#ff006e] rounded-lg hover:bg-[#ff006e20] transition-all"
|
||||
title="Remove mnemonic"
|
||||
>
|
||||
<X size={14} />
|
||||
</button>
|
||||
</div>
|
||||
{entry.error && <p className="text-xs text-[#ff006e] px-1">{entry.error}</p>}
|
||||
</div>
|
||||
@@ -348,7 +363,7 @@ export function SeedBlender({ onDirtyStateChange, setMnemonicForBackup, requestT
|
||||
<div className="p-6 bg-[#16213e] rounded-xl border-2 border-[#00f0ff]/30">
|
||||
<h3 className="font-semibold text-lg mb-4 text-[#00f0ff]" style={{ textShadow: '0 0 10px rgba(0,240,255,0.7)' }}>Step 3: Input Dice Rolls</h3>
|
||||
<div className="space-y-4">
|
||||
<textarea value={diceRolls} onChange={(e) => setDiceRolls(e.target.value.replace(/[^1-6]/g, ''))} placeholder="Enter 99+ dice rolls (e.g., 16345...)" className="w-full h-32 p-3 bg-[#16213e] border-2 border-[#00f0ff]/50 rounded-lg text-lg font-mono text-[#00f0ff] placeholder-[#9d84b7]" />
|
||||
<textarea value={diceRolls} onChange={(e) => setDiceRolls(e.target.value.replace(/[^1-6]/g, ''))} placeholder="99+ dice rolls (e.g., 16345...)" className="w-full h-32 p-3 bg-[#16213e] border-2 border-[#00f0ff]/50 rounded-lg font-mono text-xs placeholder:text-[10px] placeholder:text-[#6ef3f7]" />
|
||||
{dicePatternWarning && (<div className="p-3 bg-[#ff006e]/10 border-2 border-[#ff006e]/30 text-[#ff006e] rounded-lg text-sm flex gap-3"><AlertTriangle /><p><span className="font-bold">Warning:</span> {dicePatternWarning}</p></div>)}
|
||||
{diceStats && diceStats.length > 0 && (<div className="grid grid-cols-2 md:grid-cols-4 gap-4 text-center"><div className="p-3 bg-[#1a1a2e] rounded-lg border-2 border-[#00f0ff]/30"><p className="text-xs text-[#6ef3f7]">Rolls</p><p className="text-lg font-bold text-[#00f0ff]">{diceStats.length}</p></div><div className="p-3 bg-[#1a1a2e] rounded-lg border-2 border-[#00f0ff]/30"><p className="text-xs text-[#6ef3f7]">Entropy (bits)</p><p className="text-lg font-bold text-[#00f0ff]">{diceStats.estimatedEntropyBits.toFixed(1)}</p></div><div className="p-3 bg-[#1a1a2e] rounded-lg border-2 border-[#00f0ff]/30"><p className="text-xs text-[#6ef3f7]">Mean</p><p className="text-lg font-bold text-[#00f0ff]">{diceStats.mean.toFixed(2)}</p></div><div className="p-3 bg-[#1a1a2e] rounded-lg border-2 border-[#00f0ff]/30"><p className="text-xs text-[#6ef3f7]">Chi-Square</p><p className={`text-lg font-bold ${diceStats.chiSquare > 11.07 ? 'text-[#ff006e]' : 'text-[#00f0ff]'}`}>{diceStats.chiSquare.toFixed(2)}</p></div></div>)}
|
||||
{diceOnlyMnemonic && (<div className="space-y-1 pt-2"><label className="text-xs font-semibold text-[#00f0ff] uppercase tracking-widest" style={{ textShadow: '0 0 10px rgba(0,240,255,0.7)' }}>Dice-Only Preview Mnemonic</label><p data-sensitive="Dice-Only Preview Mnemonic" className="p-3 bg-[#1a1a2e] rounded-md font-mono text-sm text-[#00f0ff] break-words border-2 border-[#00f0ff]/30">{diceOnlyMnemonic}</p></div>)}
|
||||
|
||||
Reference in New Issue
Block a user