feat: transform UI to dark cyberpunk theme

- Eliminate all white/light boxes and backgrounds
- Fix drag-drop zone with neon cyberpunk colors (#00f0ff, #ff006e, #16213e)
- Fix restored mnemonic display with matrix green (#39ff14)
- Fix security options panel with dark gradient
- Fix all remaining slate-700/slate-800 labels to cyberpunk neon
- Fix info banners and text colors
- Update badge components with cyberpunk color scheme
- Apply consistent dark theme across all components
This commit is contained in:
LC mac
2026-02-08 22:27:41 +08:00
parent 0ab99ce493
commit 7c4fc1460c
12 changed files with 649 additions and 515 deletions

View File

@@ -253,82 +253,82 @@ export function SeedBlender({ onDirtyStateChange, setMnemonicForBackup, requestT
};
const getBorderColor = (isValid: boolean | null) => {
if (isValid === true) return 'border-green-500 focus:ring-green-500';
if (isValid === false) return 'border-red-500 focus:ring-red-500';
return 'border-slate-200 focus:ring-teal-500';
if (isValid === true) return 'border-[#39ff14] focus:ring-[#39ff14]';
if (isValid === false) return 'border-[#ff006e] focus:ring-[#ff006e]';
return 'border-[#00f0ff]/50 focus:ring-[#00f0ff]';
};
return (
<>
<div className="space-y-6 pb-20">
<div className="flex items-center justify-between">
<h2 className="text-2xl font-bold text-slate-100">Seed Blender</h2>
<button onClick={handleLockAndClear} className="flex items-center gap-2 text-sm text-red-400 bg-slate-800/50 px-3 py-1.5 rounded-lg hover:bg-red-900/50"><Lock size={16} /><span>Lock/Clear</span></button>
<h2 className="text-2xl font-bold text-[#00f0ff]" style={{ textShadow: '0 0 10px rgba(0,240,255,0.7)' }}>Seed Blender</h2>
<button onClick={handleLockAndClear} className="flex items-center gap-2 text-sm text-[#ff006e] bg-[#16213e] px-3 py-1.5 rounded-lg hover:bg-[#ff006e]/20 border-2 border-[#ff006e]/50"><Lock size={16} /><span>Lock/Clear</span></button>
</div>
<div className="p-6 bg-slate-700/50 rounded-xl border border-slate-600">
<h3 className="font-semibold text-lg mb-4 text-slate-200">Step 1: Input Mnemonics</h3>
<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 1: Input Mnemonics</h3>
<div className="space-y-4">
{entries.map((entry, index) => (
<div key={entry.id} className="p-3 bg-slate-800/50 rounded-lg">
<div key={entry.id} className="p-3 bg-[#1a1a2e] rounded-lg border-2 border-[#00f0ff]/20">
{entry.passwordRequired ? (
<div className="space-y-2">
<div className="flex items-center justify-between"><label className="text-sm font-semibold text-slate-200">Decrypt {entry.inputType.toUpperCase()} Mnemonic</label><button onClick={() => updateEntry(index, createNewEntry())} className="text-xs text-slate-400 hover:text-white">&times; Cancel</button></div>
<p className="text-xs text-slate-400 truncate">Payload: <code className="text-slate-300">{entry.rawInput.substring(0, 40)}...</code></p>
<div className="flex gap-2"><input type="password" placeholder="Enter passphrase to decrypt..." value={entry.passwordInput} onChange={(e) => updateEntry(index, { passwordInput: e.target.value })} className="w-full p-2 bg-slate-50 border-2 border-slate-200 rounded-lg text-sm font-mono text-slate-900 focus:outline-none focus:ring-2 focus:ring-purple-500" /><button onClick={() => handleDecrypt(index)} className="px-4 bg-purple-600 text-white rounded-lg font-semibold hover:bg-purple-700"><Key size={16}/></button></div>
{entry.error && <p className="text-xs text-red-400">{entry.error}</p>}
<div className="flex items-center justify-between"><label className="text-sm font-semibold text-[#00f0ff]">Decrypt {entry.inputType.toUpperCase()} Mnemonic</label><button onClick={() => updateEntry(index, createNewEntry())} className="text-xs text-[#6ef3f7] hover:text-[#00f0ff]">&times; Cancel</button></div>
<p className="text-xs text-[#6ef3f7] truncate">Payload: <code className="text-[#9d84b7]">{entry.rawInput.substring(0, 40)}...</code></p>
<div className="flex gap-2"><input type="password" placeholder="Enter passphrase to decrypt..." value={entry.passwordInput} onChange={(e) => updateEntry(index, { passwordInput: e.target.value })} className="w-full p-2 bg-[#16213e] border-2 border-[#00f0ff]/50 rounded-lg text-sm font-mono text-[#00f0ff] placeholder-[#9d84b7] focus:outline-none focus:border-[#ff006e] focus:shadow-[0_0_20px_rgba(255,0,110,0.5)]" /><button onClick={() => handleDecrypt(index)} className="px-4 bg-[#ff006e] text-white rounded-lg font-semibold hover:bg-[#ff4d8f] hover:shadow-[0_0_15px_rgba(255,0,110,0.5)]"><Key size={16}/></button></div>
{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-slate-50 border-2 rounded-lg text-sm font-mono text-slate-900 placeholder:text-slate-400 ${getBorderColor(entry.isValid)}`} />
{entry.isValid === true && <CheckCircle2 className="absolute top-3 right-3 text-green-500" />}
{entry.isValid === false && <AlertTriangle className="absolute top-3 right-3 text-red-500" />}
<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-purple-600/20 text-purple-300 hover:bg-purple-600/50 hover:text-white rounded-md"><QrCode size={20} /></button>
<button onClick={() => handleRemoveEntry(entry.id)} className="p-3 h-full bg-red-600/20 text-red-400 hover:bg-red-600/50 hover:text-white rounded-md"><X size={20} /></button>
<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>
{entry.error && <p className="text-xs text-red-400 px-1">{entry.error}</p>}
{entry.error && <p className="text-xs text-[#ff006e] px-1">{entry.error}</p>}
</div>
)}
</div>
))}
<button onClick={handleAddEntry} className="w-full py-2.5 bg-slate-600/70 hover:bg-slate-600 rounded-lg font-semibold flex items-center justify-center gap-2"><Plus size={16} /> Add Another Mnemonic</button>
<button onClick={handleAddEntry} className="w-full py-2.5 bg-[#1a1a2e] hover:bg-[#16213e] text-[#00f0ff] rounded-lg font-semibold flex items-center justify-center gap-2 border-2 border-[#00f0ff]/50"><Plus size={16} /> Add Another Mnemonic</button>
</div>
</div>
<div className="p-6 bg-slate-700/50 rounded-xl border border-slate-600 min-h-[10rem]">
<h3 className="font-semibold text-lg mb-4 text-slate-200">Step 2: Blended Preview</h3>
{blending ? <p className="text-sm text-slate-400">Blending...</p> : !blendError && blendedResult ? (<div className="space-y-4 animate-in fade-in">{xorStrength?.isWeak && (<div className="p-3 bg-amber-500/10 border border-amber-500/30 text-amber-300 rounded-lg text-sm flex gap-3"><AlertTriangle /><div><span className="font-bold">Weak XOR Result:</span> Detected only {xorStrength.uniqueBytes} unique bytes. This can happen if seeds are identical or too similar.</div></div>)}<div className="space-y-1"><label className="text-xs font-semibold text-slate-400">Blended Mnemonic (12-word)</label><p data-sensitive="Blended Mnemonic (12-word)" className="p-3 bg-slate-800 rounded-md font-mono text-sm text-slate-100 break-words">{blendedResult.blendedMnemonic12}</p></div>{blendedResult.blendedMnemonic24 && (<div className="space-y-1"><label className="text-xs font-semibold text-slate-400">Blended Mnemonic (24-word)</label><p data-sensitive="Blended Mnemonic (24-word)" className="p-3 bg-slate-800 rounded-md font-mono text-sm text-slate-100 break-words">{blendedResult.blendedMnemonic24}</p></div>)}</div>) : (<p className="text-sm text-slate-400">{blendError || 'Previews will appear here once you enter one or more valid mnemonics.'}</p>)}
<div className="p-6 bg-[#16213e] rounded-xl border-2 border-[#00f0ff]/30 min-h-[10rem]">
<h3 className="font-semibold text-lg mb-4 text-[#00f0ff]" style={{ textShadow: '0 0 10px rgba(0,240,255,0.7)' }}>Step 2: Blended Preview</h3>
{blending ? <p className="text-sm text-[#6ef3f7]">Blending...</p> : !blendError && blendedResult ? (<div className="space-y-4 animate-in fade-in">{xorStrength?.isWeak && (<div className="p-3 bg-[#ff006e]/10 border-2 border-[#ff006e]/30 text-[#ff006e] rounded-lg text-sm flex gap-3"><AlertTriangle /><div><span className="font-bold">Weak XOR Result:</span> Detected only {xorStrength.uniqueBytes} unique bytes. This can happen if seeds are identical or too similar.</div></div>)}<div className="space-y-1"><label className="text-xs font-semibold text-[#00f0ff] uppercase tracking-widest" style={{ textShadow: '0 0 10px rgba(0,240,255,0.7)' }}>Blended Mnemonic (12-word)</label><p data-sensitive="Blended Mnemonic (12-word)" className="p-3 bg-[#1a1a2e] rounded-md font-mono text-sm text-[#00f0ff] break-words border-2 border-[#00f0ff]/30">{blendedResult.blendedMnemonic12}</p></div>{blendedResult.blendedMnemonic24 && (<div className="space-y-1"><label className="text-xs font-semibold text-[#00f0ff] uppercase tracking-widest" style={{ textShadow: '0 0 10px rgba(0,240,255,0.7)' }}>Blended Mnemonic (24-word)</label><p data-sensitive="Blended Mnemonic (24-word)" className="p-3 bg-[#1a1a2e] rounded-md font-mono text-sm text-[#00f0ff] break-words border-2 border-[#00f0ff]/30">{blendedResult.blendedMnemonic24}</p></div>)}</div>) : (<p className="text-sm text-[#6ef3f7]">{blendError || 'Previews will appear here once you enter one or more valid mnemonics.'}</p>)}
</div>
<div className="p-6 bg-slate-700/50 rounded-xl border border-slate-600">
<h3 className="font-semibold text-lg mb-4 text-slate-200">Step 3: Input Dice Rolls</h3>
<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-slate-50 border-2 border-slate-200 rounded-lg text-lg font-mono text-slate-900 placeholder:text-slate-400" />
{dicePatternWarning && (<div className="p-3 bg-amber-500/10 border border-amber-500/30 text-amber-300 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-slate-800 rounded-lg"><p className="text-xs text-slate-400">Rolls</p><p className="text-lg font-bold">{diceStats.length}</p></div><div className="p-3 bg-slate-800 rounded-lg"><p className="text-xs text-slate-400">Entropy (bits)</p><p className="text-lg font-bold">{diceStats.estimatedEntropyBits.toFixed(1)}</p></div><div className="p-3 bg-slate-800 rounded-lg"><p className="text-xs text-slate-400">Mean</p><p className="text-lg font-bold">{diceStats.mean.toFixed(2)}</p></div><div className="p-3 bg-slate-800 rounded-lg"><p className="text-xs text-slate-400">Chi-Square</p><p className={`text-lg font-bold ${diceStats.chiSquare > 11.07 ? 'text-amber-400' : ''}`}>{diceStats.chiSquare.toFixed(2)}</p></div></div>)}
{diceOnlyMnemonic && (<div className="space-y-1 pt-2"><label className="text-xs font-semibold text-slate-400">Dice-Only Preview Mnemonic</label><p data-sensitive="Dice-Only Preview Mnemonic" className="p-3 bg-slate-800 rounded-md font-mono text-sm text-slate-100 break-words">{diceOnlyMnemonic}</p></div>)}
<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]" />
{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>)}
</div>
</div>
<div className="p-6 bg-slate-900/70 rounded-xl border-2 border-teal-500/50 shadow-lg">
<h3 className="font-semibold text-lg mb-4 text-slate-200">Step 4: Generate Final Mnemonic</h3>
<div className="p-6 bg-[#16213e] rounded-xl border-2 border-[#00f0ff]/50 shadow-[0_0_20px_rgba(0,240,255,0.3)]">
<h3 className="font-semibold text-lg mb-4 text-[#00f0ff]" style={{ textShadow: '0 0 10px rgba(0,240,255,0.7)' }}>Step 4: Generate Final Mnemonic</h3>
{finalMnemonic ? (
<div className="p-4 bg-gradient-to-br from-green-50 to-emerald-50 border-2 border-green-300 rounded-2xl shadow-lg">
<div className="flex items-center justify-between mb-4"><span className="font-bold text-green-700 flex items-center gap-2 text-lg"><CheckCircle2 size={22} /> Final Mnemonic Generated</span><button onClick={() => setFinalMnemonic(null)} className="p-2.5 hover:bg-green-100 rounded-xl"><EyeOff size={22} /></button></div>
<div className="p-6 bg-white rounded-xl border-2 border-green-200 shadow-sm"><p data-sensitive="Final Blended Mnemonic" className="font-mono text-center text-lg text-slate-800 break-words">{finalMnemonic}</p></div>
<div className="mt-4 p-3 bg-red-500/10 text-red-300 rounded-lg text-xs flex gap-2"><AlertTriangle size={16} className="shrink-0 mt-0.5" /><span><strong>Security Warning:</strong> Write this down immediately. Do not save it digitally.</span></div>
<div className="p-4 bg-[#0a0a0f] border-2 border-[#39ff14] rounded-2xl shadow-[0_0_20px_rgba(57,255,20,0.3)]">
<div className="flex items-center justify-between mb-4"><span className="font-bold text-[#39ff14] flex items-center gap-2 text-lg" style={{ textShadow: '0 0 10px rgba(57,255,20,0.8)' }}><CheckCircle2 size={22} /> Final Mnemonic Generated</span><button onClick={() => setFinalMnemonic(null)} className="p-2.5 hover:bg-[#16213e] rounded-xl transition-all text-[#39ff14] hover:shadow-[0_0_15px_rgba(57,255,20,0.5)] flex items-center gap-2"><EyeOff size={22} /> Hide</button></div>
<div className="p-6 bg-[#0a0a0f] rounded-xl border-2 border-[#39ff14] shadow-[0_0_20px_rgba(57,255,20,0.3)]"><p data-sensitive="Final Blended Mnemonic" className="font-mono text-center text-lg break-words text-[#39ff14]">{finalMnemonic}</p></div>
<div className="mt-4 p-3 bg-[#ff006e]/10 text-[#ff006e] rounded-lg text-xs flex gap-2 border-2 border-[#ff006e]/30"><AlertTriangle size={16} className="shrink-0 mt-0.5" /><span><strong>Security Warning:</strong> Write this down immediately. Do not save it digitally.</span></div>
<div className="grid grid-cols-2 gap-3 mt-4">
<button onClick={() => setShowFinalQR(true)} className="w-full py-2.5 bg-slate-700 text-white rounded-lg font-semibold flex items-center justify-center gap-2"><QrCode size={16}/> Export as QR</button>
<button onClick={handleTransfer} className="w-full py-2.5 bg-teal-600 text-white rounded-lg font-semibold flex items-center justify-center gap-2"><ArrowRight size={16}/> Transfer to Backup</button>
<button onClick={() => setShowFinalQR(true)} className="w-full py-2.5 bg-[#1a1a2e] text-[#00f0ff] rounded-lg font-semibold flex items-center justify-center gap-2 border-2 border-[#00f0ff]/50 hover:bg-[#16213e] hover:shadow-[0_0_15px_rgba(0,240,255,0.3)]"><QrCode size={16}/> Export as QR</button>
<button onClick={handleTransfer} className="w-full py-2.5 bg-[#00f0ff] text-[#16213e] rounded-lg font-semibold flex items-center justify-center gap-2 border-2 border-[#00f0ff] hover:bg-[#00f0ff]/80 hover:shadow-[0_0_15px_rgba(0,240,255,0.5)]"><ArrowRight size={16}/> Transfer to Backup</button>
</div>
</div>
) : (
<><p className="text-sm text-slate-400 mb-4">Once you have entered valid mnemonics and at least 50 dice rolls, you can generate the final mnemonic.</p><button onClick={handleFinalMix} disabled={!blendedResult || !diceRolls || diceRolls.length < 50 || mixing} className="w-full py-3 bg-gradient-to-r from-teal-500 to-cyan-600 text-white rounded-xl font-bold flex items-center justify-center gap-2 disabled:opacity-50">{mixing ? <RefreshCw className="animate-spin" size={20} /> : <Sparkles size={20} />}{mixing ? 'Generating...' : 'Mix Mnemonic + Dice'}</button></>
<><p className="text-sm text-[#6ef3f7] mb-4">Once you have entered valid mnemonics and at least 50 dice rolls, you can generate the final mnemonic.</p><button onClick={handleFinalMix} disabled={!blendedResult || !diceRolls || diceRolls.length < 50 || mixing} className="w-full py-3 bg-gradient-to-r from-[#00f0ff] to-[#0066ff] text-[#16213e] rounded-xl font-bold flex items-center justify-center gap-2 disabled:opacity-50 hover:shadow-[0_0_20px_rgba(0,240,255,0.5)]">{mixing ? <RefreshCw className="animate-spin" size={20} /> : <Sparkles size={20} />}{mixing ? 'Generating...' : 'Mix Mnemonic + Dice'}</button></>
)}
</div>
</div>
@@ -340,11 +340,11 @@ export function SeedBlender({ onDirtyStateChange, setMnemonicForBackup, requestT
/>}
{showFinalQR && finalMnemonic && (
<div className="fixed inset-0 bg-black/60 z-50 flex items-center justify-center" onClick={() => setShowFinalQR(false)}>
<div className="bg-white rounded-2xl p-4" onClick={e => e.stopPropagation()}>
<div className="bg-[#16213e] rounded-2xl p-4 border-2 border-[#00f0ff]/50" onClick={e => e.stopPropagation()}>
<QrDisplay value={finalMnemonic} />
</div>
</div>
)}
</>
);
}
}