mirror of
https://github.com/kccleoc/seedpgp-web.git
synced 2026-03-07 09:57:50 +08:00
- Change button labels: 'Extra secure' / 'Normal' (better reflects defense-in-depth) - Update tooltips to acknowledge CSP already blocks connections: - 'Extra secure: Added manual blocking layer (CSP already blocks connections)' - 'Normal: Relying on CSP to block connections' - Update comment: Clarify button adds extra manual layer, not primary control - More transparent about how security actually works (CSP does the real work)
167 lines
6.8 KiB
TypeScript
167 lines
6.8 KiB
TypeScript
import React from 'react';
|
|
import { Shield, RefreshCw } from 'lucide-react';
|
|
import SecurityBadge from './badges/SecurityBadge';
|
|
import StorageBadge from './badges/StorageBadge';
|
|
import ClipboardBadge from './badges/ClipboardBadge';
|
|
|
|
interface StorageItem {
|
|
key: string;
|
|
value: string;
|
|
size: number;
|
|
isSensitive: boolean;
|
|
}
|
|
|
|
interface ClipboardEvent {
|
|
timestamp: Date;
|
|
field: string;
|
|
length: number;
|
|
}
|
|
|
|
interface HeaderProps {
|
|
onOpenSecurityModal: () => void;
|
|
onOpenStorageModal: () => void;
|
|
localItems: StorageItem[];
|
|
sessionItems: StorageItem[];
|
|
events: ClipboardEvent[];
|
|
onOpenClipboardModal: () => void;
|
|
activeTab: 'create' | 'backup' | 'restore' | 'seedblender';
|
|
onRequestTabChange: (tab: 'create' | 'backup' | 'restore' | 'seedblender') => void;
|
|
appVersion: string;
|
|
isNetworkBlocked: boolean;
|
|
onToggleNetwork: () => void;
|
|
onResetAll: () => void; // NEW
|
|
}
|
|
|
|
const Header: React.FC<HeaderProps> = ({
|
|
onOpenSecurityModal,
|
|
onOpenStorageModal,
|
|
localItems,
|
|
sessionItems,
|
|
events,
|
|
onOpenClipboardModal,
|
|
activeTab,
|
|
onRequestTabChange,
|
|
appVersion,
|
|
isNetworkBlocked,
|
|
onToggleNetwork,
|
|
onResetAll
|
|
}) => {
|
|
return (
|
|
<header className="sticky top-0 z-[100] bg-[#0a0a0f] border-b border-[#00f0ff30] backdrop-blur-sm">
|
|
<div className="w-full px-4 py-3 space-y-3">
|
|
|
|
{/* ROW 1: Logo + App Info (LEFT) | Reset (RIGHT) */}
|
|
<div className="flex items-center justify-between">
|
|
<div className="flex items-center gap-3">
|
|
<div className="w-10 h-10 bg-[#00f0ff] rounded-lg flex items-center justify-center shadow-[0_0_15px_rgba(0,240,255,0.5)]">
|
|
<Shield className="w-6 h-6 text-[#0a0a0f]" />
|
|
</div>
|
|
<div>
|
|
<h1 className="text-lg font-semibold text-[#00f0ff]" style={{ textShadow: '0 0 10px rgba(0,240,255,0.7)' }}>
|
|
SeedPGP <span className="text-[#ff006e]">{appVersion}</span>
|
|
</h1>
|
|
<p className="text-xs text-[#6ef3f7]">OpenPGP-secured BIP39 backup</p>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Reset button - top right */}
|
|
<button
|
|
onClick={onResetAll}
|
|
className="flex items-center gap-1.5 px-3 py-1.5 text-xs bg-[#16213e] border border-[#ff006e] text-[#ff006e] rounded-lg font-medium hover:bg-[#ff006e20] transition-all"
|
|
title="Reset all data"
|
|
>
|
|
<RefreshCw size={12} />
|
|
<span className="hidden sm:inline">Reset</span>
|
|
</button>
|
|
</div>
|
|
|
|
{/* ROW 2: Badges (LEFT) | Action Buttons (RIGHT) */}
|
|
<div className="flex items-center gap-2 pb-2 border-b border-[#00f0ff20]">
|
|
{/* Left: Monitoring Badges */}
|
|
<div className="flex items-center gap-2">
|
|
<SecurityBadge onClick={onOpenSecurityModal} />
|
|
<div onClick={onOpenStorageModal} className="cursor-pointer">
|
|
<StorageBadge localItems={localItems} sessionItems={sessionItems} />
|
|
</div>
|
|
<div onClick={onOpenClipboardModal} className="cursor-pointer">
|
|
<ClipboardBadge events={events} onOpenClipboardModal={onOpenClipboardModal} />
|
|
</div>
|
|
</div>
|
|
|
|
{/* Spacer - pushes right content to the right */}
|
|
<div className="flex-1"></div>
|
|
|
|
{/* Right: Action Buttons */}
|
|
<div className="flex items-center gap-2">
|
|
{/* Defense-in-depth toggle: Add extra manual blocking layer on top of CSP */}
|
|
<button
|
|
onClick={onToggleNetwork}
|
|
className={`flex items-center gap-1 px-2.5 py-1.5 text-xs rounded-lg font-medium transition-all whitespace-nowrap ${isNetworkBlocked
|
|
? 'bg-[#16213e] border border-[#ff006e] text-[#ff006e] hover:bg-[#ff006e20]'
|
|
: 'bg-[#16213e] border border-[#39ff14] text-[#39ff14] hover:bg-[#39ff1420]'
|
|
}`}
|
|
title={isNetworkBlocked
|
|
? 'Extra secure: Added manual blocking layer (CSP already blocks connections)'
|
|
: 'Normal: Relying on CSP to block connections'}
|
|
>
|
|
<span className="text-sm">{isNetworkBlocked ? '🚫' : '🌐'}</span>
|
|
<span className="hidden sm:inline text-[10px]">
|
|
{isNetworkBlocked ? 'Extra secure' : 'Normal'}
|
|
</span>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
{/* ROW 3: Navigation Tabs */}
|
|
<div className="grid grid-cols-4 gap-2">
|
|
<button
|
|
className={`py-2 rounded-lg font-medium text-xs whitespace-nowrap transition-all ${activeTab === 'create'
|
|
? 'bg-[#1a1a2e] text-[#00f0ff] border-2 border-[#ff006e] shadow-[0_0_15px_rgba(255,0,110,0.5)]'
|
|
: 'bg-[#0a0a0f] text-[#9d84b7] border-2 border-[#00f0ff30] hover:text-[#6ef3f7] hover:border-[#00f0ff50]'
|
|
}`}
|
|
style={activeTab === 'create' ? { textShadow: '0 0 10px rgba(0,240,255,0.8)' } : undefined}
|
|
onClick={() => onRequestTabChange('create')}
|
|
>
|
|
Create
|
|
</button>
|
|
|
|
<button
|
|
className={`py-2 rounded-lg font-medium text-xs whitespace-nowrap transition-all ${activeTab === 'backup'
|
|
? 'bg-[#1a1a2e] text-[#00f0ff] border-2 border-[#ff006e] shadow-[0_0_15px_rgba(255,0,110,0.5)]'
|
|
: 'bg-[#0a0a0f] text-[#9d84b7] border-2 border-[#00f0ff30] hover:text-[#6ef3f7] hover:border-[#00f0ff50]'
|
|
}`}
|
|
style={activeTab === 'backup' ? { textShadow: '0 0 10px rgba(0,240,255,0.8)' } : undefined}
|
|
onClick={() => onRequestTabChange('backup')}
|
|
>
|
|
Backup
|
|
</button>
|
|
|
|
<button
|
|
className={`py-2 rounded-lg font-medium text-xs whitespace-nowrap transition-all ${activeTab === 'restore'
|
|
? 'bg-[#1a1a2e] text-[#00f0ff] border-2 border-[#ff006e] shadow-[0_0_15px_rgba(255,0,110,0.5)]'
|
|
: 'bg-[#0a0a0f] text-[#9d84b7] border-2 border-[#00f0ff30] hover:text-[#6ef3f7] hover:border-[#00f0ff50]'
|
|
}`}
|
|
style={activeTab === 'restore' ? { textShadow: '0 0 10px rgba(0,240,255,0.8)' } : undefined}
|
|
onClick={() => onRequestTabChange('restore')}
|
|
>
|
|
Restore
|
|
</button>
|
|
|
|
<button
|
|
className={`py-2 rounded-lg font-medium text-xs whitespace-nowrap transition-all ${activeTab === 'seedblender'
|
|
? 'bg-[#1a1a2e] text-[#00f0ff] border-2 border-[#ff006e] shadow-[0_0_15px_rgba(255,0,110,0.5)]'
|
|
: 'bg-[#0a0a0f] text-[#9d84b7] border-2 border-[#00f0ff30] hover:text-[#6ef3f7] hover:border-[#00f0ff50]'
|
|
}`}
|
|
style={activeTab === 'seedblender' ? { textShadow: '0 0 10px rgba(0,240,255,0.8)' } : undefined}
|
|
onClick={() => onRequestTabChange('seedblender')}
|
|
>
|
|
Blender
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</header>
|
|
);
|
|
};
|
|
|
|
export default Header;
|