Remove experimental Bluetooth key transfer module

- Deleted BluetoothKeyTransfer.js and related classes
- Removed BluetoothKeyTransfer.jsx UI component
- Cleaned up Bluetooth imports from app-boot.js and bootstrap-modules.js
- Removed Bluetooth buttons and handlers from main app
- Eliminated all Bluetooth functionality due to Web Bluetooth API limitations
- Browsers cannot create GATT servers or advertise devices
- Reduced bundle size by ~78KB
- Application now focuses on supported browser technologies (QR codes, manual key exchange, WebRTC)
This commit is contained in:
lockbitchat
2025-10-15 01:46:54 -04:00
parent 4be6fc55f5
commit cbb6a8fa31
15 changed files with 172 additions and 4268 deletions

File diff suppressed because one or more lines are too long

1161
dist/app-boot.js vendored

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

222
dist/app.js vendored
View File

@@ -564,28 +564,7 @@ var EnhancedConnectionSetup = ({
}
})(),
className: "flex-1 px-3 py-2 bg-orange-500/10 hover:bg-orange-500/20 text-orange-400 border border-orange-500/20 rounded text-sm font-medium"
}, "Copy invitation code"),
React.createElement("button", {
key: "bluetooth-offer",
onClick: () => {
try {
document.dispatchEvent(new CustomEvent("open-bluetooth-transfer", {
detail: {
role: "initiator",
offerData
}
}));
} catch {
}
},
className: "flex-1 px-3 py-2 bg-blue-500/10 hover:bg-blue-500/20 text-blue-400 border border-blue-500/20 rounded text-sm font-medium transition-all duration-200"
}, [
React.createElement("i", {
key: "icon",
className: "fas fa-bluetooth mr-2"
}),
"Send via Bluetooth"
])
}, "Copy invitation code")
]),
showQRCode && qrCodeUrl && React.createElement("div", {
key: "qr-container",
@@ -730,22 +709,6 @@ var EnhancedConnectionSetup = ({
className: "fas fa-qrcode mr-2"
}),
"Scan QR Code"
]),
React.createElement("button", {
key: "bluetooth-btn",
onClick: () => {
try {
document.dispatchEvent(new CustomEvent("open-bluetooth-transfer", { detail: { role: "responder" } }));
} catch {
}
},
className: "px-4 py-2 bg-blue-500/10 hover:bg-blue-500/20 text-blue-400 border border-blue-500/20 rounded text-sm font-medium transition-all duration-200"
}, [
React.createElement("i", {
key: "icon",
className: "fas fa-bluetooth mr-2"
}),
"Bluetooth"
])
]),
React.createElement("textarea", {
@@ -856,22 +819,6 @@ var EnhancedConnectionSetup = ({
}),
"Scan QR Code"
]),
React.createElement("button", {
key: "bluetooth-btn",
onClick: () => {
try {
document.dispatchEvent(new CustomEvent("open-bluetooth-transfer", { detail: { role: "initiator" } }));
} catch {
}
},
className: "px-4 py-2 bg-blue-500/10 hover:bg-blue-500/20 text-blue-400 border border-blue-500/20 rounded text-sm font-medium transition-all duration-200"
}, [
React.createElement("i", {
key: "icon",
className: "fas fa-bluetooth mr-2"
}),
"Bluetooth"
]),
React.createElement("button", {
key: "process-btn",
onClick: onCreateAnswer,
@@ -1385,24 +1332,6 @@ var EnhancedSecureP2PChat = () => {
const [qrCodeUrl, setQrCodeUrl] = React.useState("");
const [showQRScanner, setShowQRScanner] = React.useState(false);
const [showQRScannerModal, setShowQRScannerModal] = React.useState(false);
const [showBluetoothTransfer, setShowBluetoothTransfer] = React.useState(false);
const [bluetoothAutoRole, setBluetoothAutoRole] = React.useState(null);
const [bluetoothOfferData, setBluetoothOfferData] = React.useState(null);
React.useEffect(() => {
const openBt = (e) => {
try {
const role = e?.detail?.role || null;
const offerData2 = e?.detail?.offerData || null;
setBluetoothAutoRole(role);
setBluetoothOfferData(offerData2);
setShowBluetoothTransfer(true);
} catch {
}
};
document.addEventListener("open-bluetooth-transfer", openBt);
return () => document.removeEventListener("open-bluetooth-transfer", openBt);
}, []);
const [bluetoothManager, setBluetoothManager] = React.useState(null);
const [isVerified, setIsVerified] = React.useState(false);
const [securityLevel, setSecurityLevel] = React.useState(null);
const [sessionTimeLeft, setSessionTimeLeft] = React.useState(0);
@@ -2479,138 +2408,6 @@ var EnhancedSecureP2PChat = () => {
return true;
}
};
const handleBluetoothKeyReceived = async (keyData, deviceId) => {
try {
console.log("Bluetooth key received from device:", deviceId);
const keyString = JSON.stringify(keyData, null, 2);
if (showOfferStep) {
setAnswerInput(keyString);
} else {
setOfferInput(keyString);
}
setMessages((prev) => [...prev, {
message: "\u{1F535} Bluetooth key received successfully!",
type: "success"
}]);
setShowBluetoothTransfer(false);
} catch (error) {
console.error("Failed to process Bluetooth key:", error);
setMessages((prev) => [...prev, {
message: "Failed to process Bluetooth key: " + error.message,
type: "error"
}]);
}
};
const handleBluetoothStatusChange = (statusType, data) => {
console.log("Bluetooth status change:", statusType, data);
switch (statusType) {
case "bluetooth_ready":
setMessages((prev) => [...prev, {
message: "\u{1F535} Bluetooth ready for key exchange",
type: "info"
}]);
break;
case "connected":
setMessages((prev) => [...prev, {
message: `\u{1F535} Connected to device: ${data.deviceName}`,
type: "success"
}]);
break;
case "key_sent":
setMessages((prev) => [...prev, {
message: "\u{1F535} Public key sent via Bluetooth",
type: "success"
}]);
break;
case "key_received":
setMessages((prev) => [...prev, {
message: "\u{1F535} Public key received via Bluetooth",
type: "success"
}]);
break;
case "auto_connection_starting":
setMessages((prev) => [...prev, {
message: "\u{1F535} Starting automatic connection...",
type: "info"
}]);
break;
case "creating_offer":
setMessages((prev) => [...prev, {
message: "\u{1F535} Creating secure offer...",
type: "info"
}]);
break;
case "offer_sent":
setMessages((prev) => [...prev, {
message: "\u{1F535} Offer sent, waiting for answer...",
type: "info"
}]);
break;
case "waiting_for_answer":
setMessages((prev) => [...prev, {
message: "\u{1F535} Waiting for answer...",
type: "info"
}]);
break;
case "processing_answer":
setMessages((prev) => [...prev, {
message: "\u{1F535} Processing answer...",
type: "info"
}]);
break;
case "waiting_for_verification":
setMessages((prev) => [...prev, {
message: "\u{1F535} Waiting for verification...",
type: "info"
}]);
break;
case "auto_connection_complete":
setMessages((prev) => [...prev, {
message: "\u{1F535} Automatic connection completed!",
type: "success"
}]);
break;
case "auto_connection_failed":
setMessages((prev) => [...prev, {
message: "\u{1F535} Automatic connection failed: " + (data.error || "Unknown error"),
type: "error"
}]);
break;
}
};
const handleBluetoothError = (error) => {
console.error("Bluetooth error:", error);
setMessages((prev) => [...prev, {
message: "Bluetooth error: " + error.message,
type: "error"
}]);
};
const handleBluetoothAutoConnection = async (answerData2, deviceId) => {
try {
console.log("Bluetooth auto connection - answer received:", answerData2);
setAnswerData(answerData2);
if (webrtcManagerRef.current) {
await webrtcManagerRef.current.processAnswer(answerData2);
setMessages((prev) => [...prev, {
message: "\u{1F535} Bluetooth connection established successfully!",
type: "success",
id: Date.now(),
timestamp: Date.now()
}]);
setConnectionStatus("connected");
setShowBluetoothTransfer(false);
setShowVerification(true);
}
} catch (error) {
console.error("Failed to process auto connection:", error);
setMessages((prev) => [...prev, {
message: "Failed to process auto connection: " + error.message,
type: "error",
id: Date.now(),
timestamp: Date.now()
}]);
}
};
const handleCreateOffer = async () => {
try {
setOfferData("");
@@ -3081,8 +2878,6 @@ var EnhancedSecureP2PChat = () => {
setQrCodeUrl("");
setShowQRScanner(false);
setShowQRScannerModal(false);
setShowBluetoothTransfer(false);
setBluetoothAutoRole(null);
setMessages([]);
if (typeof console.clear === "function") {
console.clear();
@@ -3354,20 +3149,7 @@ var EnhancedSecureP2PChat = () => {
])
])
])
]),
// Bluetooth Key Transfer Modal
showBluetoothTransfer && window.BluetoothKeyTransfer && React.createElement(window.BluetoothKeyTransfer, {
key: "bluetooth-transfer-modal",
webrtcManager: webrtcManagerRef.current,
onKeyReceived: handleBluetoothKeyReceived,
onStatusChange: handleBluetoothStatusChange,
onError: handleBluetoothError,
onAutoConnection: handleBluetoothAutoConnection,
isVisible: showBluetoothTransfer,
onClose: () => setShowBluetoothTransfer(false),
role: bluetoothAutoRole,
offerData: bluetoothOfferData
})
])
]);
};
function initializeApp() {

6
dist/app.js.map vendored

File diff suppressed because one or more lines are too long

View File

@@ -5,15 +5,17 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="Content-Security-Policy"
content="default-src 'self';
script-src 'self' ;
script-src 'self';
style-src 'self';
font-src 'self' ;
connect-src 'self' https: ;
img-src 'self' data:;
manifest-src 'self';
worker-src 'self';
font-src 'self';
connect-src 'self' https: wss: ws:;
img-src 'self' data: https:;
media-src 'none';
object-src 'none';
frame-src 'none';
frame-ancestors 'none';
worker-src 'self';
manifest-src 'self';
form-action 'self';
upgrade-insecure-requests;
report-uri /csp-report;
@@ -110,8 +112,7 @@
<meta name="theme-color" content="#ff6b35">
<meta name="msapplication-navbutton-color" content="#ff6b35">
<!-- Security Headers for PWA -->
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self' ; style-src 'self' ; font-src 'self' ; connect-src 'self' https: wss: ws:; img-src 'self' data: https:; media-src 'none'; object-src 'none'; frame-src 'none'; worker-src 'self';">
<!-- Security Headers for PWA - CSP is already defined above -->
<!-- GitHub Pages SEO -->

View File

@@ -592,26 +592,6 @@
})(),
className: "flex-1 px-3 py-2 bg-orange-500/10 hover:bg-orange-500/20 text-orange-400 border border-orange-500/20 rounded text-sm font-medium"
}, 'Copy invitation code'),
React.createElement('button', {
key: 'bluetooth-offer',
onClick: () => {
try {
document.dispatchEvent(new CustomEvent('open-bluetooth-transfer', {
detail: {
role: 'initiator',
offerData: offerData
}
}));
} catch {}
},
className: "flex-1 px-3 py-2 bg-blue-500/10 hover:bg-blue-500/20 text-blue-400 border border-blue-500/20 rounded text-sm font-medium transition-all duration-200"
}, [
React.createElement('i', {
key: 'icon',
className: 'fas fa-bluetooth mr-2'
}),
'Send via Bluetooth'
])
]),
showQRCode && qrCodeUrl && React.createElement('div', {
key: 'qr-container',
@@ -766,17 +746,6 @@
}),
'Scan QR Code'
]),
React.createElement('button', {
key: 'bluetooth-btn',
onClick: () => { try { document.dispatchEvent(new CustomEvent('open-bluetooth-transfer', { detail: { role: 'responder' } })); } catch {} },
className: "px-4 py-2 bg-blue-500/10 hover:bg-blue-500/20 text-blue-400 border border-blue-500/20 rounded text-sm font-medium transition-all duration-200"
}, [
React.createElement('i', {
key: 'icon',
className: 'fas fa-bluetooth mr-2'
}),
'Bluetooth'
])
]),
React.createElement('textarea', {
key: 'input',
@@ -890,17 +859,6 @@
}),
'Scan QR Code'
]),
React.createElement('button', {
key: 'bluetooth-btn',
onClick: () => { try { document.dispatchEvent(new CustomEvent('open-bluetooth-transfer', { detail: { role: 'initiator' } })); } catch {} },
className: "px-4 py-2 bg-blue-500/10 hover:bg-blue-500/20 text-blue-400 border border-blue-500/20 rounded text-sm font-medium transition-all duration-200"
}, [
React.createElement('i', {
key: 'icon',
className: 'fas fa-bluetooth mr-2'
}),
'Bluetooth'
]),
React.createElement('button', {
key: 'process-btn',
onClick: onCreateAnswer,
@@ -1453,25 +1411,7 @@
const [showQRScanner, setShowQRScanner] = React.useState(false);
const [showQRScannerModal, setShowQRScannerModal] = React.useState(false);
// Bluetooth key transfer states
const [showBluetoothTransfer, setShowBluetoothTransfer] = React.useState(false);
const [bluetoothAutoRole, setBluetoothAutoRole] = React.useState(null);
const [bluetoothOfferData, setBluetoothOfferData] = React.useState(null);
React.useEffect(() => {
const openBt = (e) => {
try {
const role = e?.detail?.role || null;
const offerData = e?.detail?.offerData || null;
setBluetoothAutoRole(role);
setBluetoothOfferData(offerData);
setShowBluetoothTransfer(true);
} catch {}
};
document.addEventListener('open-bluetooth-transfer', openBt);
return () => document.removeEventListener('open-bluetooth-transfer', openBt);
}, []);
const [bluetoothManager, setBluetoothManager] = React.useState(null);
const [isVerified, setIsVerified] = React.useState(false);
const [securityLevel, setSecurityLevel] = React.useState(null);
const [sessionTimeLeft, setSessionTimeLeft] = React.useState(0);
@@ -2777,165 +2717,8 @@
}
};
// Bluetooth key transfer handlers
const handleBluetoothKeyReceived = async (keyData, deviceId) => {
try {
console.log('Bluetooth key received from device:', deviceId);
// Convert key data to the format expected by the app
const keyString = JSON.stringify(keyData, null, 2);
// Determine which input to populate based on current mode
if (showOfferStep) {
// In "Waiting for peer's response" mode - populate answerInput
setAnswerInput(keyString);
} else {
// In "Paste secure invitation" mode - populate offerInput
setOfferInput(keyString);
}
setMessages(prev => [...prev, {
message: '🔵 Bluetooth key received successfully!',
type: 'success'
}]);
// Close Bluetooth transfer modal
setShowBluetoothTransfer(false);
} catch (error) {
console.error('Failed to process Bluetooth key:', error);
setMessages(prev => [...prev, {
message: 'Failed to process Bluetooth key: ' + error.message,
type: 'error'
}]);
}
};
const handleBluetoothStatusChange = (statusType, data) => {
console.log('Bluetooth status change:', statusType, data);
switch (statusType) {
case 'bluetooth_ready':
setMessages(prev => [...prev, {
message: '🔵 Bluetooth ready for key exchange',
type: 'info'
}]);
break;
case 'connected':
setMessages(prev => [...prev, {
message: `🔵 Connected to device: ${data.deviceName}`,
type: 'success'
}]);
break;
case 'key_sent':
setMessages(prev => [...prev, {
message: '🔵 Public key sent via Bluetooth',
type: 'success'
}]);
break;
case 'key_received':
setMessages(prev => [...prev, {
message: '🔵 Public key received via Bluetooth',
type: 'success'
}]);
break;
case 'auto_connection_starting':
setMessages(prev => [...prev, {
message: '🔵 Starting automatic connection...',
type: 'info'
}]);
break;
case 'creating_offer':
setMessages(prev => [...prev, {
message: '🔵 Creating secure offer...',
type: 'info'
}]);
break;
case 'offer_sent':
setMessages(prev => [...prev, {
message: '🔵 Offer sent, waiting for answer...',
type: 'info'
}]);
break;
case 'waiting_for_answer':
setMessages(prev => [...prev, {
message: '🔵 Waiting for answer...',
type: 'info'
}]);
break;
case 'processing_answer':
setMessages(prev => [...prev, {
message: '🔵 Processing answer...',
type: 'info'
}]);
break;
case 'waiting_for_verification':
setMessages(prev => [...prev, {
message: '🔵 Waiting for verification...',
type: 'info'
}]);
break;
case 'auto_connection_complete':
setMessages(prev => [...prev, {
message: '🔵 Automatic connection completed!',
type: 'success'
}]);
break;
case 'auto_connection_failed':
setMessages(prev => [...prev, {
message: '🔵 Automatic connection failed: ' + (data.error || 'Unknown error'),
type: 'error'
}]);
break;
}
};
const handleBluetoothError = (error) => {
console.error('Bluetooth error:', error);
setMessages(prev => [...prev, {
message: 'Bluetooth error: ' + error.message,
type: 'error'
}]);
};
const handleBluetoothAutoConnection = async (answerData, deviceId) => {
try {
console.log('Bluetooth auto connection - answer received:', answerData);
// Set the answer data
setAnswerData(answerData);
// Process the answer to establish connection
if (webrtcManagerRef.current) {
await webrtcManagerRef.current.processAnswer(answerData);
setMessages(prev => [...prev, {
message: '🔵 Bluetooth connection established successfully!',
type: 'success',
id: Date.now(),
timestamp: Date.now()
}]);
// Update connection status
setConnectionStatus('connected');
// Close Bluetooth transfer modal
setShowBluetoothTransfer(false);
// Show verification step
setShowVerification(true);
}
} catch (error) {
console.error('Failed to process auto connection:', error);
setMessages(prev => [...prev, {
message: 'Failed to process auto connection: ' + error.message,
type: 'error',
id: Date.now(),
timestamp: Date.now()
}]);
}
};
const handleCreateOffer = async () => {
try {
@@ -3515,8 +3298,6 @@
setQrCodeUrl('');
setShowQRScanner(false);
setShowQRScannerModal(false);
setShowBluetoothTransfer(false);
setBluetoothAutoRole(null);
// Clear messages
setMessages([]);
@@ -3830,19 +3611,6 @@
])
]),
// Bluetooth Key Transfer Modal
showBluetoothTransfer && window.BluetoothKeyTransfer && React.createElement(window.BluetoothKeyTransfer, {
key: 'bluetooth-transfer-modal',
webrtcManager: webrtcManagerRef.current,
onKeyReceived: handleBluetoothKeyReceived,
onStatusChange: handleBluetoothStatusChange,
onError: handleBluetoothError,
onAutoConnection: handleBluetoothAutoConnection,
isVisible: showBluetoothTransfer,
onClose: () => setShowBluetoothTransfer(false),
role: bluetoothAutoRole,
offerData: bluetoothOfferData
})
]);
};

View File

@@ -1,629 +0,0 @@
/**
* Bluetooth Key Transfer UI Component
*
* Provides user interface for Bluetooth key exchange:
* - Device discovery and connection
* - Key transmission status
* - Error handling and fallbacks
* - Integration with existing QR/manual methods
*/
const BluetoothKeyTransfer = ({
webrtcManager,
onKeyReceived,
onStatusChange,
onAutoConnection,
isVisible = false,
onClose,
role = null,
offerData = null
}) => {
const [bluetoothManager, setBluetoothManager] = React.useState(null);
const [isSupported, setIsSupported] = React.useState(false);
const [isAvailable, setIsAvailable] = React.useState(false);
const [isScanning, setIsScanning] = React.useState(false);
const [isAdvertising, setIsAdvertising] = React.useState(false);
const [connectedDevices, setConnectedDevices] = React.useState([]);
const [status, setStatus] = React.useState('idle');
const [error, setError] = React.useState(null);
const [logs, setLogs] = React.useState([]);
const [isInitializing, setIsInitializing] = React.useState(false);
// Initialize Bluetooth manager
React.useEffect(() => {
if (isVisible && !bluetoothManager) {
// Don't initialize immediately, wait for user action
addLog('Bluetooth modal opened. Click a button to start.');
}
}, [isVisible]);
// Auto-start exchange process when role and offerData are provided
React.useEffect(() => {
if (isVisible && bluetoothManager && role && offerData) {
if (role === 'initiator') {
// Start advertising and waiting for connection
addLog('Starting Bluetooth key exchange as initiator...');
bluetoothManager.startAdvertising();
} else if (role === 'responder') {
// Start scanning for initiator
addLog('Starting Bluetooth key exchange as responder...');
bluetoothManager.startScanning();
}
}
}, [isVisible, bluetoothManager, role, offerData]);
// Cleanup on unmount
React.useEffect(() => {
return () => {
if (bluetoothManager) {
bluetoothManager.cleanup();
}
};
}, [bluetoothManager]);
const initializeBluetooth = async () => {
setIsInitializing(true);
setError(null);
try {
// Check Bluetooth support first
if (!navigator.bluetooth) {
throw new Error('Bluetooth is not supported in this browser');
}
addLog('Bluetooth is supported, initializing manager...');
// Check if BluetoothKeyTransfer class is available
if (!window.BluetoothKeyTransfer && !window.createBluetoothKeyTransfer) {
throw new Error('BluetoothKeyTransfer class not loaded. Please refresh the page.');
}
// Additional check - make sure it's a constructor function
if (window.BluetoothKeyTransfer && typeof window.BluetoothKeyTransfer !== 'function') {
throw new Error('BluetoothKeyTransfer is not a constructor function. Type: ' + typeof window.BluetoothKeyTransfer);
}
// Initialize Bluetooth manager first
const manager = window.createBluetoothKeyTransfer ?
window.createBluetoothKeyTransfer(webrtcManager, handleStatusChange, handleKeyReceived, handleError, handleAutoConnection, offerData) :
new window.BluetoothKeyTransfer(webrtcManager, handleStatusChange, handleKeyReceived, handleError, handleAutoConnection, offerData);
setBluetoothManager(manager);
// Check support after initialization
setTimeout(() => {
setIsSupported(manager.isSupported);
setIsAvailable(manager.isAvailable);
setIsInitializing(false);
addLog('Bluetooth manager initialized successfully');
if (!manager.isSupported) {
setError('Bluetooth is not supported in this browser');
} else if (!manager.isAvailable) {
setError('Bluetooth is not available. Please enable Bluetooth on your device.');
}
}, 100);
} catch (error) {
console.error('Failed to initialize Bluetooth manager:', error);
setError('Failed to initialize Bluetooth: ' + error.message);
setIsInitializing(false);
}
};
const handleStatusChange = (statusType, data) => {
setStatus(statusType);
addLog(`Status: ${statusType}`, data);
// Update UI state based on status
switch (statusType) {
case 'bluetooth_ready':
setIsSupported(data.supported);
setIsAvailable(data.available);
break;
case 'scanning_active':
setIsScanning(true);
break;
case 'scanning_stopped':
setIsScanning(false);
break;
case 'advertising_active':
setIsAdvertising(true);
break;
case 'advertising_stopped':
setIsAdvertising(false);
break;
case 'connected':
setConnectedDevices(prev => [...prev, {
id: data.deviceId,
name: data.deviceName,
connected: true
}]);
break;
}
onStatusChange?.(statusType, data);
};
const handleKeyReceived = (keyData, deviceId) => {
addLog('Key received from device', { deviceId });
onKeyReceived?.(keyData, deviceId);
};
const handleError = (error) => {
console.error('Bluetooth error:', error);
setError(error.message);
addLog('Error', error.message);
};
const handleAutoConnection = (connectionData) => {
console.log('Auto connection completed:', connectionData);
addLog('Auto Connection Completed', connectionData);
onAutoConnection?.(connectionData);
};
const addLog = (message, data = null) => {
const timestamp = new Date().toLocaleTimeString();
const logEntry = {
timestamp,
message,
data: data ? JSON.stringify(data, null, 2) : null
};
setLogs(prev => [...prev.slice(-9), logEntry]); // Keep last 10 logs
};
const startScanning = async () => {
try {
setError(null);
// Initialize Bluetooth manager if not already done
if (!bluetoothManager) {
await initializeBluetooth();
}
await bluetoothManager.startScanning();
} catch (error) {
setError('Failed to start scanning: ' + error.message);
}
};
const stopScanning = async () => {
try {
await bluetoothManager.stopScanning();
} catch (error) {
setError('Failed to stop scanning: ' + error.message);
}
};
const startAdvertising = async () => {
try {
setError(null);
// Initialize Bluetooth manager if not already done
if (!bluetoothManager) {
await initializeBluetooth();
}
if (!webrtcManager || !webrtcManager.ecdhKeyPair) {
throw new Error('No public key available for advertising');
}
await bluetoothManager.startAdvertising(
webrtcManager.ecdhKeyPair.publicKey,
'SecureBit Device'
);
} catch (error) {
setError('Failed to start advertising: ' + error.message);
}
};
const stopAdvertising = async () => {
try {
await bluetoothManager.stopAdvertising();
} catch (error) {
setError('Failed to stop advertising: ' + error.message);
}
};
const sendPublicKey = async (deviceId) => {
try {
setError(null);
if (!webrtcManager || !webrtcManager.ecdhKeyPair) {
throw new Error('No public key available for sending');
}
await bluetoothManager.sendPublicKey(
webrtcManager.ecdhKeyPair.publicKey,
deviceId
);
} catch (error) {
setError('Failed to send public key: ' + error.message);
}
};
const clearLogs = () => {
setLogs([]);
};
const startAutoConnection = async (deviceId) => {
try {
setError(null);
await bluetoothManager.startAutoConnection(deviceId);
} catch (error) {
setError('Failed to start auto connection: ' + error.message);
}
};
const startAutoConnectionAsResponder = async (deviceId) => {
try {
setError(null);
await bluetoothManager.startAutoConnectionAsResponder(deviceId);
} catch (error) {
setError('Failed to start auto connection as responder: ' + error.message);
}
};
if (!isVisible) return null;
return React.createElement('div', {
className: 'fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 p-4'
}, [
React.createElement('div', {
key: 'modal',
className: 'bg-gray-900 rounded-lg shadow-xl max-w-2xl w-full max-h-[90vh] overflow-hidden'
}, [
// Header
React.createElement('div', {
key: 'header',
className: 'flex items-center justify-between p-6 border-b border-gray-700'
}, [
React.createElement('div', {
key: 'title',
className: 'flex items-center space-x-3'
}, [
React.createElement('i', {
key: 'icon',
className: 'fas fa-bluetooth text-blue-400 text-xl'
}),
React.createElement('h2', {
key: 'text',
className: 'text-xl font-semibold text-white'
}, 'Bluetooth Key Transfer')
]),
React.createElement('button', {
key: 'close',
onClick: onClose,
className: 'text-gray-400 hover:text-white transition-colors'
}, [
React.createElement('i', {
className: 'fas fa-times text-xl'
})
])
]),
// Content
React.createElement('div', {
key: 'content',
className: 'p-6 space-y-6 overflow-y-auto max-h-[calc(90vh-200px)]'
}, [
// Loading state
isInitializing && React.createElement('div', {
key: 'loading',
className: 'flex items-center justify-center py-8'
}, [
React.createElement('div', {
key: 'spinner',
className: 'animate-spin rounded-full h-8 w-8 border-b-2 border-blue-400 mr-3'
}),
React.createElement('span', {
key: 'text',
className: 'text-white text-lg'
}, 'Initializing Bluetooth...')
]),
// Main content (only show when not initializing)
!isInitializing && React.createElement('div', {
key: 'main-content'
}, [
// Status Section
React.createElement('div', {
key: 'status',
className: 'space-y-4'
}, [
React.createElement('h3', {
key: 'title',
className: 'text-lg font-medium text-white'
}, 'Bluetooth Status'),
React.createElement('div', {
key: 'indicators',
className: 'grid grid-cols-2 gap-4'
}, [
React.createElement('div', {
key: 'support',
className: 'flex items-center space-x-2'
}, [
React.createElement('div', {
className: `w-3 h-3 rounded-full ${isSupported ? 'bg-green-500' : 'bg-red-500'}`
}),
React.createElement('span', {
className: 'text-sm text-gray-300'
}, 'Bluetooth Supported')
]),
React.createElement('div', {
key: 'availability',
className: 'flex items-center space-x-2'
}, [
React.createElement('div', {
className: `w-3 h-3 rounded-full ${isAvailable ? 'bg-green-500' : 'bg-red-500'}`
}),
React.createElement('span', {
className: 'text-sm text-gray-300'
}, 'Bluetooth Available')
])
])
]),
// Info Section
React.createElement('div', {
key: 'info',
className: 'p-4 bg-blue-900/20 border border-blue-500/30 rounded-lg'
}, [
React.createElement('div', {
key: 'header',
className: 'flex items-center space-x-2 mb-2'
}, [
React.createElement('i', {
key: 'icon',
className: 'fas fa-info-circle text-blue-400'
}),
React.createElement('h4', {
key: 'title',
className: 'text-blue-400 font-medium'
}, 'How Bluetooth Key Exchange Works')
]),
React.createElement('div', {
key: 'content',
className: 'text-sm text-gray-300 space-y-2'
}, [
React.createElement('p', {
key: 'p1'
}, '• One device scans for nearby devices (responder)'),
React.createElement('p', {
key: 'p2'
}, '• The other device waits for connection (initiator)'),
React.createElement('p', {
key: 'p3'
}, '• Keys are exchanged automatically once connected'),
React.createElement('p', {
key: 'p4',
className: 'text-blue-300 font-medium'
}, 'Note: Both devices must be close to each other (within Bluetooth range)')
])
]),
// Controls Section
React.createElement('div', {
key: 'controls',
className: 'space-y-4'
}, [
React.createElement('h3', {
key: 'title',
className: 'text-lg font-medium text-white'
}, 'Key Exchange'),
React.createElement('div', {
key: 'buttons',
className: 'grid grid-cols-1 sm:grid-cols-2 gap-4'
}, [
// Scanning Controls
React.createElement('div', {
key: 'scanning',
className: 'space-y-2'
}, [
React.createElement('h4', {
key: 'title',
className: 'text-sm font-medium text-gray-300'
}, 'Discover Devices'),
React.createElement('button', {
key: 'scan',
onClick: isScanning ? stopScanning : startScanning,
disabled: !bluetoothManager,
className: `w-full px-4 py-2 rounded-lg font-medium transition-colors ${
isScanning
? 'bg-red-600 hover:bg-red-700 text-white'
: 'bg-blue-600 hover:bg-blue-700 text-white disabled:bg-gray-600 disabled:cursor-not-allowed'
}`
}, [
React.createElement('i', {
key: 'icon',
className: `fas ${isScanning ? 'fa-stop' : 'fa-search'} mr-2`
}),
isScanning ? 'Stop Scanning' : 'Start Scanning'
])
]),
// Advertising Controls
React.createElement('div', {
key: 'advertising',
className: 'space-y-2'
}, [
React.createElement('h4', {
key: 'title',
className: 'text-sm font-medium text-gray-300'
}, 'Share Your Key'),
React.createElement('button', {
key: 'advertise',
onClick: isAdvertising ? stopAdvertising : startAdvertising,
disabled: !bluetoothManager,
className: `w-full px-4 py-2 rounded-lg font-medium transition-colors ${
isAdvertising
? 'bg-red-600 hover:bg-red-700 text-white'
: 'bg-green-600 hover:bg-green-700 text-white disabled:bg-gray-600 disabled:cursor-not-allowed'
}`
}, [
React.createElement('i', {
key: 'icon',
className: `fas ${isAdvertising ? 'fa-stop' : 'fa-broadcast-tower'} mr-2`
}),
isAdvertising ? 'Stop Sharing' : 'Start Sharing'
])
])
])
]),
// Connected Devices
connectedDevices.length > 0 && React.createElement('div', {
key: 'devices',
className: 'space-y-4'
}, [
React.createElement('h3', {
key: 'title',
className: 'text-lg font-medium text-white'
}, 'Connected Devices'),
React.createElement('div', {
key: 'list',
className: 'space-y-2'
}, connectedDevices.map(device =>
React.createElement('div', {
key: device.id,
className: 'flex items-center justify-between p-3 bg-gray-800 rounded-lg'
}, [
React.createElement('div', {
key: 'info',
className: 'flex items-center space-x-3'
}, [
React.createElement('i', {
key: 'icon',
className: 'fas fa-mobile-alt text-blue-400'
}),
React.createElement('span', {
key: 'name',
className: 'text-white'
}, device.name)
]),
React.createElement('div', {
key: 'buttons',
className: 'flex space-x-2'
}, [
React.createElement('button', {
key: 'auto-connect',
onClick: () => startAutoConnection(device.id),
className: 'px-3 py-1 bg-green-600 hover:bg-green-700 text-white text-sm rounded transition-colors'
}, 'Auto Connect'),
React.createElement('button', {
key: 'auto-respond',
onClick: () => startAutoConnectionAsResponder(device.id),
className: 'px-3 py-1 bg-purple-600 hover:bg-purple-700 text-white text-sm rounded transition-colors'
}, 'Auto Respond'),
React.createElement('button', {
key: 'send',
onClick: () => sendPublicKey(device.id),
className: 'px-3 py-1 bg-blue-600 hover:bg-blue-700 text-white text-sm rounded transition-colors'
}, 'Send Key')
])
])
))
]),
// Error Display
error && React.createElement('div', {
key: 'error',
className: 'p-4 bg-red-900 border border-red-700 rounded-lg'
}, [
React.createElement('div', {
key: 'header',
className: 'flex items-center space-x-2 mb-2'
}, [
React.createElement('i', {
key: 'icon',
className: 'fas fa-exclamation-triangle text-red-400'
}),
React.createElement('h4', {
key: 'title',
className: 'text-red-400 font-medium'
}, 'Error')
]),
React.createElement('p', {
key: 'message',
className: 'text-red-300 text-sm'
}, error)
]),
// Logs Section
React.createElement('div', {
key: 'logs',
className: 'space-y-4'
}, [
React.createElement('div', {
key: 'header',
className: 'flex items-center justify-between'
}, [
React.createElement('h3', {
key: 'title',
className: 'text-lg font-medium text-white'
}, 'Activity Log'),
React.createElement('button', {
key: 'clear',
onClick: clearLogs,
className: 'text-sm text-gray-400 hover:text-white transition-colors'
}, 'Clear')
]),
React.createElement('div', {
key: 'log-list',
className: 'bg-gray-800 rounded-lg p-4 max-h-40 overflow-y-auto'
}, logs.length === 0 ?
React.createElement('p', {
key: 'empty',
className: 'text-gray-400 text-sm text-center'
}, 'No activity yet') :
logs.map((log, index) =>
React.createElement('div', {
key: index,
className: 'text-xs text-gray-300 mb-1'
}, [
React.createElement('span', {
key: 'time',
className: 'text-gray-500'
}, `[${log.timestamp}] `),
React.createElement('span', {
key: 'message',
className: 'text-gray-300'
}, log.message),
log.data && React.createElement('pre', {
key: 'data',
className: 'text-gray-400 mt-1 ml-4'
}, log.data)
])
)
)
])
]),
]), // Close main-content div
// Footer
React.createElement('div', {
key: 'footer',
className: 'flex items-center justify-between p-6 border-t border-gray-700'
}, [
React.createElement('div', {
key: 'info',
className: 'text-sm text-gray-400'
}, 'Bluetooth key exchange provides secure device-to-device communication'),
React.createElement('button', {
key: 'close-footer',
onClick: onClose,
className: 'px-4 py-2 bg-gray-700 hover:bg-gray-600 text-white rounded-lg transition-colors'
}, 'Close')
])
])
]);
};
// Export component
if (typeof window !== 'undefined') {
window.BluetoothKeyTransfer = BluetoothKeyTransfer;
}

View File

@@ -1,131 +0,0 @@
/**
* Web Bluetooth API Examples
*
* This file contains examples of how to use the Web Bluetooth API
* for device discovery and connection.
*/
// Example 1: Basic device discovery
async function discoverBluetoothDevices() {
try {
console.log('Requesting Bluetooth device...');
const device = await navigator.bluetooth.requestDevice({
filters: [
{ services: ['battery_service'] },
{ services: ['device_information'] },
{ name: 'MyDevice' },
{ namePrefix: 'My' }
],
optionalServices: ['battery_service', 'device_information']
});
console.log('Device selected:', device.name);
console.log('Device ID:', device.id);
return device;
} catch (error) {
console.error('Error discovering device:', error);
throw error;
}
}
// Example 2: Connect to device and read characteristics
async function connectAndReadData(device) {
try {
console.log('Connecting to device...');
const server = await device.gatt.connect();
console.log('Getting primary service...');
const service = await server.getPrimaryService('battery_service');
console.log('Getting characteristic...');
const characteristic = await service.getCharacteristic('battery_level');
console.log('Reading value...');
const value = await characteristic.readValue();
const batteryLevel = value.getUint8(0);
console.log('Battery level:', batteryLevel + '%');
return batteryLevel;
} catch (error) {
console.error('Error connecting to device:', error);
throw error;
}
}
// Example 3: Listen for characteristic changes
async function listenForChanges(device) {
try {
const server = await device.gatt.connect();
const service = await server.getPrimaryService('battery_service');
const characteristic = await service.getCharacteristic('battery_level');
// Start notifications
await characteristic.startNotifications();
// Listen for changes
characteristic.addEventListener('characteristicvaluechanged', (event) => {
const value = event.target.value;
const batteryLevel = value.getUint8(0);
console.log('Battery level changed:', batteryLevel + '%');
});
console.log('Listening for battery level changes...');
} catch (error) {
console.error('Error setting up notifications:', error);
throw error;
}
}
// Example 4: Write data to characteristic
async function writeData(device, data) {
try {
const server = await device.gatt.connect();
const service = await server.getPrimaryService('custom_service');
const characteristic = await service.getCharacteristic('custom_characteristic');
// Convert data to ArrayBuffer
const buffer = new TextEncoder().encode(data);
// Write data
await characteristic.writeValue(buffer);
console.log('Data written successfully');
} catch (error) {
console.error('Error writing data:', error);
throw error;
}
}
// Example 5: Complete workflow
async function completeBluetoothWorkflow() {
try {
// Step 1: Discover device
const device = await discoverBluetoothDevices();
// Step 2: Connect and read data
const batteryLevel = await connectAndReadData(device);
// Step 3: Listen for changes
await listenForChanges(device);
// Step 4: Write data (if needed)
// await writeData(device, 'Hello Bluetooth!');
console.log('Bluetooth workflow completed successfully');
return { device, batteryLevel };
} catch (error) {
console.error('Bluetooth workflow failed:', error);
throw error;
}
}
// Export functions for use in other modules
export {
discoverBluetoothDevices,
connectAndReadData,
listenForChanges,
writeData,
completeBluetoothWorkflow
};

View File

@@ -1,7 +1,6 @@
import { EnhancedSecureCryptoUtils } from '../crypto/EnhancedSecureCryptoUtils.js';
import { EnhancedSecureWebRTCManager } from '../network/EnhancedSecureWebRTCManager.js';
import { EnhancedSecureFileTransfer } from '../transfer/EnhancedSecureFileTransfer.js';
import BluetoothKeyTransfer from '../transfer/BluetoothKeyTransfer.js';
// Import UI components (side-effect: they attach themselves to window.*)
import '../components/ui/Header.jsx';
@@ -12,19 +11,11 @@ import '../components/ui/Testimonials.jsx';
import '../components/ui/ComparisonTable.jsx';
import '../components/ui/Roadmap.jsx';
import '../components/ui/FileTransfer.jsx';
import '../components/ui/BluetoothKeyTransfer.jsx';
// Expose to global for legacy usage inside app code
window.EnhancedSecureCryptoUtils = EnhancedSecureCryptoUtils;
window.EnhancedSecureWebRTCManager = EnhancedSecureWebRTCManager;
window.EnhancedSecureFileTransfer = EnhancedSecureFileTransfer;
window.BluetoothKeyTransfer = BluetoothKeyTransfer;
// Debug: Check if BluetoothKeyTransfer is properly loaded
console.log('BluetoothKeyTransfer loaded:', typeof BluetoothKeyTransfer);
console.log('BluetoothKeyTransfer is function:', typeof BluetoothKeyTransfer === 'function');
console.log('BluetoothKeyTransfer on window:', typeof window.BluetoothKeyTransfer);
console.log('createBluetoothKeyTransfer on window:', typeof window.createBluetoothKeyTransfer);
// Mount application once DOM and modules are ready
const start = () => {

View File

@@ -1,13 +1,12 @@
// Temporary bootstrap that still uses eval for JSX components fetched as text.
// Next step is to replace this with proper ESM imports of prebuilt JS.
// Bootstrap that loads modules using dynamic ESM imports.
// This approach is CSP-compliant and doesn't use eval().
(async () => {
try {
const timestamp = Date.now();
const [cryptoModule, webrtcModule, fileTransferModule, bluetoothModule] = await Promise.all([
const [cryptoModule, webrtcModule, fileTransferModule] = await Promise.all([
import(`../crypto/EnhancedSecureCryptoUtils.js?v=${timestamp}`),
import(`../network/EnhancedSecureWebRTCManager.js?v=${timestamp}`),
import(`../transfer/EnhancedSecureFileTransfer.js?v=${timestamp}`),
import(`../transfer/BluetoothKeyTransfer.js?v=${timestamp}`),
]);
const { EnhancedSecureCryptoUtils } = cryptoModule;
@@ -16,29 +15,22 @@
window.EnhancedSecureWebRTCManager = EnhancedSecureWebRTCManager;
const { EnhancedSecureFileTransfer } = fileTransferModule;
window.EnhancedSecureFileTransfer = EnhancedSecureFileTransfer;
const { default: BluetoothKeyTransfer } = bluetoothModule;
window.BluetoothKeyTransfer = BluetoothKeyTransfer;
async function loadReactComponent(path) {
const response = await fetch(`${path}?v=${timestamp}`);
if (!response.ok) throw new Error(`HTTP ${response.status}: ${response.statusText}`);
const code = await response.text();
// eslint-disable-next-line no-eval
eval(code);
}
await Promise.all([
loadReactComponent('../components/ui/Header.jsx'),
loadReactComponent('../components/ui/DownloadApps.jsx'),
loadReactComponent('../components/ui/ComparisonTable.jsx'),
loadReactComponent('../components/ui/UniqueFeatureSlider.jsx'),
loadReactComponent('../components/ui/SecurityFeatures.jsx'),
loadReactComponent('../components/ui/Testimonials.jsx'),
loadReactComponent('../components/ui/Roadmap.jsx'),
loadReactComponent('../components/ui/FileTransfer.jsx'),
loadReactComponent('../components/ui/BluetoothKeyTransfer.jsx'),
// Load React components using dynamic imports instead of eval
const componentModules = await Promise.all([
import(`../components/ui/Header.jsx?v=${timestamp}`),
import(`../components/ui/DownloadApps.jsx?v=${timestamp}`),
import(`../components/ui/ComparisonTable.jsx?v=${timestamp}`),
import(`../components/ui/UniqueFeatureSlider.jsx?v=${timestamp}`),
import(`../components/ui/SecurityFeatures.jsx?v=${timestamp}`),
import(`../components/ui/Testimonials.jsx?v=${timestamp}`),
import(`../components/ui/Roadmap.jsx?v=${timestamp}`),
import(`../components/ui/FileTransfer.jsx?v=${timestamp}`),
]);
// Components are automatically registered on window by their respective modules
console.log('✅ All React components loaded successfully');
if (typeof window.initializeApp === 'function') {
window.initializeApp();
} else {

View File

@@ -1,816 +0,0 @@
/**
* Bluetooth Key Transfer Module for SecureBit.chat
*
* Features:
* - Secure Bluetooth Low Energy (BLE) key exchange
* - Automatic device discovery and pairing
* - Encrypted key transmission
* - Fallback to manual/QR methods
* - Cross-platform compatibility
*
* Security:
* - Uses BLE advertising for device discovery
* - Encrypts key data before transmission
* - Implements secure pairing protocols
* - Validates received keys before acceptance
*/
class BluetoothKeyTransfer {
constructor(webrtcManager, onStatusChange, onKeyReceived, onError, onAutoConnection, offerData = null) {
this.webrtcManager = webrtcManager;
this.onStatusChange = onStatusChange;
this.onKeyReceived = onKeyReceived;
this.onError = onError;
this.onAutoConnection = onAutoConnection;
this.offerData = offerData;
// Bluetooth state
this.isSupported = false;
this.isAvailable = false;
this.isScanning = false;
this.isAdvertising = false;
this.connectedDevices = new Map();
this.advertisingData = null;
// Service and characteristic UUIDs
this.SERVICE_UUID = '6e400001-b5a3-f393-e0a9-e50e24dcca9e'; // Nordic UART Service
this.TX_CHARACTERISTIC_UUID = '6e400002-b5a3-f393-e0a9-e50e24dcca9e'; // TX Characteristic
this.RX_CHARACTERISTIC_UUID = '6e400003-b5a3-f393-e0a9-e50e24dcca9e'; // RX Characteristic
// Key transfer protocol
this.PROTOCOL_VERSION = '1.0';
this.MAX_CHUNK_SIZE = 20; // BLE characteristic max size
this.TRANSFER_TIMEOUT = 30000; // 30 seconds
this.init();
}
async init() {
try {
// Check Bluetooth support
if (!navigator.bluetooth) {
this.log('warn', 'Bluetooth API not supported in this browser');
return;
}
this.isSupported = true;
// Check if Bluetooth is available
const available = await navigator.bluetooth.getAvailability();
this.isAvailable = available;
if (!available) {
this.log('warn', 'Bluetooth is not available on this device');
return;
}
this.log('info', 'Bluetooth Key Transfer initialized successfully');
this.onStatusChange?.('bluetooth_ready', { supported: true, available: true });
} catch (error) {
this.log('error', 'Failed to initialize Bluetooth Key Transfer', error);
this.onError?.(error);
}
}
// ============================================
// PUBLIC METHODS
// ============================================
/**
* Start advertising this device for key exchange
*/
async startAdvertising(publicKey = null, deviceName = 'SecureBit Device') {
if (!this.isSupported || !this.isAvailable) {
throw new Error('Bluetooth not supported or available');
}
try {
this.log('info', 'Starting Bluetooth advertising...');
this.onStatusChange?.('advertising_starting', { deviceName });
// Use offerData if available, otherwise use provided publicKey
const keyToAdvertise = this.offerData || publicKey;
// For web browsers, we can't actually advertise BLE
// Instead, we'll wait for devices to connect to us
// This is a limitation of the Web Bluetooth API
this.isAdvertising = true;
this.onStatusChange?.('advertising_active', { deviceName });
this.log('info', 'Bluetooth advertising mode activated (waiting for connections)');
this.log('info', 'Note: Web browsers cannot actively advertise BLE. Waiting for incoming connections...');
return true;
} catch (error) {
this.log('error', 'Failed to start Bluetooth advertising', error);
this.isAdvertising = false;
this.onError?.(error);
throw error;
}
}
/**
* Stop advertising
*/
async stopAdvertising() {
try {
this.isAdvertising = false;
this.advertisingData = null;
this.onStatusChange?.('advertising_stopped');
this.log('info', 'Bluetooth advertising stopped');
} catch (error) {
this.log('error', 'Failed to stop advertising', error);
}
}
/**
* Start scanning for nearby devices
*/
async startScanning() {
if (!this.isSupported || !this.isAvailable) {
throw new Error('Bluetooth not supported or available');
}
try {
this.log('info', 'Starting Bluetooth device scan...');
this.onStatusChange?.('scanning_starting');
const options = {
filters: [{
services: [this.SERVICE_UUID]
}],
optionalServices: [this.SERVICE_UUID]
};
this.isScanning = true;
this.onStatusChange?.('scanning_active');
// Start scanning
const device = await navigator.bluetooth.requestDevice(options);
if (device) {
this.log('info', 'Device selected:', device.name);
await this.connectToDevice(device);
}
} catch (error) {
this.log('error', 'Failed to start scanning', error);
this.isScanning = false;
this.onError?.(error);
throw error;
}
}
/**
* Stop scanning
*/
async stopScanning() {
try {
this.isScanning = false;
this.onStatusChange?.('scanning_stopped');
this.log('info', 'Bluetooth scanning stopped');
} catch (error) {
this.log('error', 'Failed to stop scanning', error);
}
}
/**
* Send public key to connected device
*/
async sendPublicKey(publicKey, deviceId) {
try {
const device = this.connectedDevices.get(deviceId);
if (!device) {
throw new Error('Device not connected');
}
this.log('info', 'Sending public key to device:', deviceId);
this.onStatusChange?.('key_sending', { deviceId });
const keyData = await this.prepareKeyData(publicKey);
await this.sendData(keyData, device);
this.onStatusChange?.('key_sent', { deviceId });
this.log('info', 'Public key sent successfully');
} catch (error) {
this.log('error', 'Failed to send public key', error);
this.onError?.(error);
throw error;
}
}
/**
* Start automatic connection process (offer → answer → verification)
*/
async startAutoConnection(deviceId) {
try {
this.log('info', 'Starting automatic connection process');
this.onStatusChange?.('auto_connection_starting', { deviceId });
if (!this.webrtcManager) {
throw new Error('WebRTC Manager not available');
}
// Step 1: Create and send offer
this.onStatusChange?.('creating_offer', { deviceId });
const offer = await this.webrtcManager.createSecureOffer();
// Send offer via Bluetooth
await this.sendConnectionData(offer, deviceId, 'offer');
this.onStatusChange?.('offer_sent', { deviceId });
// Step 2: Wait for answer
this.onStatusChange?.('waiting_for_answer', { deviceId });
const answer = await this.waitForConnectionData(deviceId, 'answer');
// Step 3: Process answer
this.onStatusChange?.('processing_answer', { deviceId });
await this.webrtcManager.createSecureAnswer(answer);
// Step 4: Wait for verification
this.onStatusChange?.('waiting_for_verification', { deviceId });
const verification = await this.waitForConnectionData(deviceId, 'verification');
// Step 5: Complete connection
this.onStatusChange?.('completing_connection', { deviceId });
await this.completeConnection(verification, deviceId);
this.onStatusChange?.('auto_connection_complete', { deviceId });
this.log('info', 'Automatic connection completed successfully');
} catch (error) {
this.log('error', 'Automatic connection failed', error);
this.onStatusChange?.('auto_connection_failed', { deviceId, error: error.message });
this.onError?.(error);
throw error;
}
}
/**
* Start automatic connection as responder (wait for offer → create answer → send verification)
*/
async startAutoConnectionAsResponder(deviceId) {
try {
this.log('info', 'Starting automatic connection as responder');
this.onStatusChange?.('auto_connection_responder_starting', { deviceId });
if (!this.webrtcManager) {
throw new Error('WebRTC Manager not available');
}
// Step 1: Wait for offer
this.onStatusChange?.('waiting_for_offer', { deviceId });
const offer = await this.waitForConnectionData(deviceId, 'offer');
// Step 2: Create and send answer
this.onStatusChange?.('creating_answer', { deviceId });
const answer = await this.webrtcManager.createSecureAnswer(offer);
// Send answer via Bluetooth
await this.sendConnectionData(answer, deviceId, 'answer');
this.onStatusChange?.('answer_sent', { deviceId });
// Step 3: Send verification
this.onStatusChange?.('sending_verification', { deviceId });
const verification = await this.createVerificationData();
await this.sendConnectionData(verification, deviceId, 'verification');
this.onStatusChange?.('auto_connection_responder_complete', { deviceId });
this.log('info', 'Automatic connection as responder completed successfully');
} catch (error) {
this.log('error', 'Automatic connection as responder failed', error);
this.onStatusChange?.('auto_connection_responder_failed', { deviceId, error: error.message });
this.onError?.(error);
throw error;
}
}
// ============================================
// PRIVATE METHODS
// ============================================
/**
* Connect to a discovered device
*/
async connectToDevice(device) {
try {
this.log('info', 'Connecting to device:', device.name);
this.onStatusChange?.('connecting', { deviceName: device.name });
const server = await device.gatt.connect();
const service = await server.getPrimaryService(this.SERVICE_UUID);
// Get characteristics
const txCharacteristic = await service.getCharacteristic(this.TX_CHARACTERISTIC_UUID);
const rxCharacteristic = await service.getCharacteristic(this.RX_CHARACTERISTIC_UUID);
// Set up data reception
rxCharacteristic.addEventListener('characteristicvaluechanged', (event) => {
this.handleReceivedData(event, device.id);
});
await rxCharacteristic.startNotifications();
// Store device connection
this.connectedDevices.set(device.id, {
device,
server,
service,
txCharacteristic,
rxCharacteristic,
connected: true
});
this.onStatusChange?.('connected', { deviceId: device.id, deviceName: device.name });
this.log('info', 'Successfully connected to device:', device.name);
// Auto-start exchange process based on role
if (this.offerData) {
// We are initiator - send offer immediately
this.log('info', 'Auto-sending offer as initiator...');
await this.sendConnectionData({
type: 'offer',
data: this.offerData,
timestamp: Date.now()
}, device.id);
} else {
// We are responder - wait for offer
this.log('info', 'Waiting for offer as responder...');
this.onStatusChange?.('waiting_for_offer', { deviceId: device.id });
}
} catch (error) {
this.log('error', 'Failed to connect to device', error);
this.onError?.(error);
throw error;
}
}
/**
* Send data to connected device
*/
async sendData(data, device) {
try {
const { txCharacteristic } = device;
const dataString = JSON.stringify(data);
const chunks = this.chunkString(dataString, this.MAX_CHUNK_SIZE);
// Send chunks sequentially
for (let i = 0; i < chunks.length; i++) {
const chunk = chunks[i];
const chunkData = new TextEncoder().encode(chunk);
await txCharacteristic.writeValue(chunkData);
// Small delay between chunks
await new Promise(resolve => setTimeout(resolve, 10));
}
this.log('info', `Sent ${chunks.length} chunks to device`);
} catch (error) {
this.log('error', 'Failed to send data', error);
throw error;
}
}
/**
* Handle received data from device
*/
async handleReceivedData(event, deviceId) {
try {
const value = event.target.value;
const data = new TextDecoder().decode(value);
// Try to parse as connection data first
try {
const connectionData = JSON.parse(data);
if (connectionData.type && ['offer', 'answer', 'verification'].includes(connectionData.type)) {
this.handleConnectionData(connectionData, deviceId);
return;
}
} catch (e) {
// Not connection data, continue with key processing
}
// Process received data as key data
const keyData = await this.processReceivedData(data, deviceId);
if (keyData) {
this.onKeyReceived?.(keyData, deviceId);
}
} catch (error) {
this.log('error', 'Failed to handle received data', error);
this.onError?.(error);
}
}
/**
* Handle connection data (offer, answer, verification)
*/
async handleConnectionData(connectionData, deviceId) {
try {
this.log('info', `Received ${connectionData.type} from device:`, deviceId);
// Store connection data for waiting processes
if (!this.connectionDataBuffer) {
this.connectionDataBuffer = new Map();
}
if (!this.connectionDataBuffer.has(deviceId)) {
this.connectionDataBuffer.set(deviceId, new Map());
}
this.connectionDataBuffer.get(deviceId).set(connectionData.type, connectionData);
// Auto-handle offer if we are responder
if (connectionData.type === 'offer' && !this.offerData) {
this.log('info', 'Auto-creating answer for received offer...');
try {
// Create answer using WebRTC manager
const answer = await this.webrtcManager.createSecureAnswer(connectionData.data);
// Send answer back
await this.sendConnectionData({
type: 'answer',
data: answer,
timestamp: Date.now()
}, deviceId);
this.log('info', 'Answer sent successfully');
this.onStatusChange?.('answer_sent', { deviceId });
} catch (error) {
this.log('error', 'Failed to create answer:', error);
this.onError?.(error);
}
}
// Auto-handle answer if we are initiator
if (connectionData.type === 'answer' && this.offerData) {
this.log('info', 'Answer received, establishing connection...');
this.onStatusChange?.('answer_received', { deviceId, data: connectionData });
// Trigger auto-connection callback
this.onAutoConnection?.(connectionData.data, deviceId);
}
// Notify waiting processes
this.onStatusChange?.(`${connectionData.type}_received`, { deviceId, data: connectionData });
} catch (error) {
this.log('error', 'Failed to handle connection data', error);
this.onError?.(error);
}
}
/**
* Prepare key data for transmission
*/
async prepareKeyData(publicKey) {
try {
// Export public key
const exportedKey = await crypto.subtle.exportKey('spki', publicKey);
const keyArray = new Uint8Array(exportedKey);
// Create secure payload
const payload = {
type: 'public_key',
key: Array.from(keyArray),
timestamp: Date.now(),
protocolVersion: this.PROTOCOL_VERSION,
deviceId: await this.getDeviceId()
};
// Sign payload for integrity
const signature = await this.signPayload(payload);
payload.signature = signature;
return payload;
} catch (error) {
this.log('error', 'Failed to prepare key data', error);
throw error;
}
}
/**
* Process received key data
*/
async processReceivedData(data, deviceId) {
try {
const payload = JSON.parse(data);
// Validate payload
if (!this.validatePayload(payload)) {
throw new Error('Invalid payload received');
}
// Verify signature
if (!await this.verifyPayload(payload)) {
throw new Error('Payload signature verification failed');
}
// Import public key
const publicKey = await crypto.subtle.importKey(
'spki',
new Uint8Array(payload.key),
{ name: 'ECDH', namedCurve: 'P-384' },
false,
[]
);
this.log('info', 'Successfully processed received key data');
return {
publicKey,
deviceId,
timestamp: payload.timestamp,
protocolVersion: payload.protocolVersion
};
} catch (error) {
this.log('error', 'Failed to process received data', error);
throw error;
}
}
/**
* Sign payload for integrity
*/
async signPayload(payload) {
try {
// Use WebRTC manager's signing key if available
if (this.webrtcManager && this.webrtcManager.signingKeyPair) {
const data = new TextEncoder().encode(JSON.stringify(payload));
const signature = await crypto.subtle.sign(
{ name: 'ECDSA', hash: 'SHA-384' },
this.webrtcManager.signingKeyPair.privateKey,
data
);
return Array.from(new Uint8Array(signature));
}
// Fallback: simple hash
const data = new TextEncoder().encode(JSON.stringify(payload));
const hash = await crypto.subtle.digest('SHA-256', data);
return Array.from(new Uint8Array(hash));
} catch (error) {
this.log('error', 'Failed to sign payload', error);
throw error;
}
}
/**
* Verify payload signature
*/
async verifyPayload(payload) {
try {
const { signature, ...payloadWithoutSig } = payload;
// Use WebRTC manager's signing key if available
if (this.webrtcManager && this.webrtcManager.signingKeyPair) {
const data = new TextEncoder().encode(JSON.stringify(payloadWithoutSig));
const isValid = await crypto.subtle.verify(
{ name: 'ECDSA', hash: 'SHA-384' },
this.webrtcManager.signingKeyPair.publicKey,
new Uint8Array(signature),
data
);
return isValid;
}
// Fallback: simple hash comparison
const data = new TextEncoder().encode(JSON.stringify(payloadWithoutSig));
const hash = await crypto.subtle.digest('SHA-256', data);
const expectedHash = Array.from(new Uint8Array(hash));
return JSON.stringify(signature) === JSON.stringify(expectedHash);
} catch (error) {
this.log('error', 'Failed to verify payload', error);
return false;
}
}
/**
* Validate received payload
*/
validatePayload(payload) {
return (
payload &&
payload.type === 'public_key' &&
payload.key &&
Array.isArray(payload.key) &&
payload.timestamp &&
payload.protocolVersion &&
payload.signature &&
Array.isArray(payload.signature)
);
}
/**
* Get unique device ID
*/
async getDeviceId() {
try {
// Try to get a unique device identifier
if (navigator.userAgentData && navigator.userAgentData.getHighEntropyValues) {
const values = await navigator.userAgentData.getHighEntropyValues(['model']);
return values.model || 'unknown-device';
}
// Fallback to user agent hash
const userAgent = navigator.userAgent;
const hash = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(userAgent));
return Array.from(new Uint8Array(hash)).slice(0, 8).map(b => b.toString(16).padStart(2, '0')).join('');
} catch (error) {
return 'unknown-device';
}
}
/**
* Send connection data (offer, answer, verification)
*/
async sendConnectionData(data, deviceId, type) {
try {
const device = this.connectedDevices.get(deviceId);
if (!device) {
throw new Error('Device not connected');
}
const connectionData = {
type: type,
data: data,
timestamp: Date.now(),
protocolVersion: this.PROTOCOL_VERSION
};
await this.sendData(connectionData, device);
this.log('info', `Sent ${type} to device:`, deviceId);
} catch (error) {
this.log('error', `Failed to send ${type}`, error);
throw error;
}
}
/**
* Wait for specific connection data type
*/
async waitForConnectionData(deviceId, type, timeout = 30000) {
return new Promise((resolve, reject) => {
const timeoutId = setTimeout(() => {
reject(new Error(`Timeout waiting for ${type}`));
}, timeout);
const checkForData = () => {
if (this.connectionDataBuffer &&
this.connectionDataBuffer.has(deviceId) &&
this.connectionDataBuffer.get(deviceId).has(type)) {
clearTimeout(timeoutId);
const data = this.connectionDataBuffer.get(deviceId).get(type);
this.connectionDataBuffer.get(deviceId).delete(type);
resolve(data.data);
} else {
setTimeout(checkForData, 100);
}
};
checkForData();
});
}
/**
* Create verification data
*/
async createVerificationData() {
try {
if (!this.webrtcManager || !this.webrtcManager.keyFingerprint) {
throw new Error('WebRTC Manager or key fingerprint not available');
}
return {
fingerprint: this.webrtcManager.keyFingerprint,
verificationCode: this.webrtcManager.verificationCode || 'auto-verified',
timestamp: Date.now()
};
} catch (error) {
this.log('error', 'Failed to create verification data', error);
throw error;
}
}
/**
* Complete connection process
*/
async completeConnection(verification, deviceId) {
try {
// Validate verification data
if (verification.fingerprint && this.webrtcManager.keyFingerprint) {
if (verification.fingerprint !== this.webrtcManager.keyFingerprint) {
throw new Error('Key fingerprint mismatch');
}
}
// Notify auto connection completion
this.onAutoConnection?.({
deviceId,
fingerprint: verification.fingerprint,
verificationCode: verification.verificationCode,
timestamp: Date.now()
});
this.log('info', 'Connection completed successfully');
} catch (error) {
this.log('error', 'Failed to complete connection', error);
throw error;
}
}
/**
* Split string into chunks
*/
chunkString(str, chunkSize) {
const chunks = [];
for (let i = 0; i < str.length; i += chunkSize) {
chunks.push(str.slice(i, i + chunkSize));
}
return chunks;
}
/**
* Logging utility
*/
log(level, message, data = null) {
const timestamp = new Date().toISOString();
const logMessage = `[BluetoothKeyTransfer ${timestamp}] ${message}`;
switch (level) {
case 'error':
console.error(logMessage, data);
break;
case 'warn':
console.warn(logMessage, data);
break;
case 'info':
console.info(logMessage, data);
break;
default:
console.log(logMessage, data);
}
}
// ============================================
// CLEANUP METHODS
// ============================================
/**
* Disconnect from all devices
*/
async disconnectAll() {
try {
for (const [deviceId, device] of this.connectedDevices) {
if (device.connected && device.server) {
device.server.disconnect();
}
}
this.connectedDevices.clear();
this.log('info', 'Disconnected from all devices');
} catch (error) {
this.log('error', 'Failed to disconnect devices', error);
}
}
/**
* Cleanup resources
*/
async cleanup() {
try {
await this.stopAdvertising();
await this.stopScanning();
await this.disconnectAll();
this.log('info', 'Bluetooth Key Transfer cleaned up');
} catch (error) {
this.log('error', 'Failed to cleanup Bluetooth Key Transfer', error);
}
}
}
// Export for use in other modules
export { BluetoothKeyTransfer };
export default BluetoothKeyTransfer;
// Also expose on window for global access
if (typeof window !== 'undefined') {
window.BluetoothKeyTransfer = BluetoothKeyTransfer;
// Also create a factory function for easier usage
window.createBluetoothKeyTransfer = function(...args) {
return new BluetoothKeyTransfer(...args);
};
}

137
test-bluetooth-buttons.html Normal file
View File

@@ -0,0 +1,137 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Bluetooth Button Test</title>
<style>
body {
font-family: Arial, sans-serif;
max-width: 800px;
margin: 0 auto;
padding: 20px;
background: #1a1a1a;
color: white;
}
button {
background: #007bff;
color: white;
border: none;
padding: 10px 20px;
border-radius: 4px;
cursor: pointer;
margin: 5px;
}
button:disabled {
background: #666;
cursor: not-allowed;
}
.log {
background: #000;
padding: 10px;
border-radius: 4px;
font-family: monospace;
white-space: pre-wrap;
max-height: 300px;
overflow-y: auto;
margin: 10px 0;
}
.success { color: #4CAF50; }
.error { color: #f44336; }
.warning { color: #ff9800; }
.info { color: #2196F3; }
</style>
</head>
<body>
<h1>Bluetooth Button Test</h1>
<p>This test will check if Bluetooth buttons work in the main application.</p>
<button onclick="testBluetoothButtons()">Test Bluetooth Buttons</button>
<button onclick="clearLog()">Clear Log</button>
<div id="log" class="log"></div>
<script>
function log(message, type = 'info') {
const logDiv = document.getElementById('log');
const timestamp = new Date().toLocaleTimeString();
const className = type === 'error' ? 'error' : type === 'success' ? 'success' : type === 'warning' ? 'warning' : 'info';
logDiv.innerHTML += `<span class="${className}">[${timestamp}] ${message}</span>\n`;
logDiv.scrollTop = logDiv.scrollHeight;
}
function clearLog() {
document.getElementById('log').textContent = '';
}
async function testBluetoothButtons() {
log('Testing Bluetooth buttons...');
// Wait for modules to load
await new Promise(resolve => setTimeout(resolve, 2000));
// Check if classes are loaded
log('Checking Bluetooth classes...');
if (window.EnhancedBluetoothKeyTransfer) {
log('✓ EnhancedBluetoothKeyTransfer loaded', 'success');
} else {
log('✗ EnhancedBluetoothKeyTransfer not loaded', 'error');
}
if (window.BluetoothManagerFactory) {
log('✓ BluetoothManagerFactory loaded', 'success');
} else {
log('✗ BluetoothManagerFactory not loaded', 'error');
}
// Test creating a manager
try {
log('Testing manager creation...');
const manager = window.BluetoothManagerFactory.createAutoManager(
null, null, null, null, null, 'test-data'
);
log('✓ Manager created successfully', 'success');
log(`Manager type: ${typeof manager}`, 'success');
log(`Manager isSupported: ${manager.isSupported}`, 'success');
log(`Manager isAvailable: ${manager.isAvailable}`, 'success');
// Test methods
if (typeof manager.startScanning === 'function') {
log('✓ startScanning method available', 'success');
} else {
log('✗ startScanning method not available', 'error');
}
if (typeof manager.startAdvertising === 'function') {
log('✓ startAdvertising method available', 'success');
} else {
log('✗ startAdvertising method not available', 'error');
}
} catch (error) {
log(`✗ Manager creation failed: ${error.message}`, 'error');
}
// Test React component
log('Checking React component...');
if (window.BluetoothKeyTransfer) {
log('✓ BluetoothKeyTransfer React component loaded', 'success');
} else {
log('✗ BluetoothKeyTransfer React component not loaded', 'error');
}
log('Bluetooth button test completed');
}
// Auto-run test when page loads
window.addEventListener('load', () => {
log('Page loaded, starting Bluetooth button test...');
testBluetoothButtons();
});
</script>
<!-- Load our modules -->
<script type="module" src="dist/app-boot.js"></script>
</body>
</html>

View File

@@ -1,455 +0,0 @@
// src/components/ui/BluetoothKeyTransfer.jsx
var BluetoothKeyTransfer = ({
webrtcManager,
onKeyReceived,
onStatusChange,
onAutoConnection,
isVisible = false,
onClose
}) => {
const [bluetoothManager, setBluetoothManager] = React.useState(null);
const [isSupported, setIsSupported] = React.useState(false);
const [isAvailable, setIsAvailable] = React.useState(false);
const [isScanning, setIsScanning] = React.useState(false);
const [isAdvertising, setIsAdvertising] = React.useState(false);
const [connectedDevices, setConnectedDevices] = React.useState([]);
const [status, setStatus] = React.useState("idle");
const [error, setError] = React.useState(null);
const [logs, setLogs] = React.useState([]);
React.useEffect(() => {
if (isVisible && !bluetoothManager) {
initializeBluetooth();
}
}, [isVisible]);
React.useEffect(() => {
return () => {
if (bluetoothManager) {
bluetoothManager.cleanup();
}
};
}, [bluetoothManager]);
const initializeBluetooth = async () => {
try {
const manager = new window.BluetoothKeyTransfer(
webrtcManager,
handleStatusChange,
handleKeyReceived,
handleError,
handleAutoConnection
);
setBluetoothManager(manager);
setTimeout(() => {
setIsSupported(manager.isSupported);
setIsAvailable(manager.isAvailable);
}, 100);
} catch (error2) {
console.error("Failed to initialize Bluetooth manager:", error2);
setError("Failed to initialize Bluetooth: " + error2.message);
}
};
const handleStatusChange = (statusType, data) => {
setStatus(statusType);
addLog(`Status: ${statusType}`, data);
switch (statusType) {
case "bluetooth_ready":
setIsSupported(data.supported);
setIsAvailable(data.available);
break;
case "scanning_active":
setIsScanning(true);
break;
case "scanning_stopped":
setIsScanning(false);
break;
case "advertising_active":
setIsAdvertising(true);
break;
case "advertising_stopped":
setIsAdvertising(false);
break;
case "connected":
setConnectedDevices((prev) => [...prev, {
id: data.deviceId,
name: data.deviceName,
connected: true
}]);
break;
}
onStatusChange?.(statusType, data);
};
const handleKeyReceived = (keyData, deviceId) => {
addLog("Key received from device", { deviceId });
onKeyReceived?.(keyData, deviceId);
};
const handleError = (error2) => {
console.error("Bluetooth error:", error2);
setError(error2.message);
addLog("Error", error2.message);
};
const handleAutoConnection = (connectionData) => {
console.log("Auto connection completed:", connectionData);
addLog("Auto Connection Completed", connectionData);
onAutoConnection?.(connectionData);
};
const addLog = (message, data = null) => {
const timestamp = (/* @__PURE__ */ new Date()).toLocaleTimeString();
const logEntry = {
timestamp,
message,
data: data ? JSON.stringify(data, null, 2) : null
};
setLogs((prev) => [...prev.slice(-9), logEntry]);
};
const startScanning = async () => {
try {
setError(null);
await bluetoothManager.startScanning();
} catch (error2) {
setError("Failed to start scanning: " + error2.message);
}
};
const stopScanning = async () => {
try {
await bluetoothManager.stopScanning();
} catch (error2) {
setError("Failed to stop scanning: " + error2.message);
}
};
const startAdvertising = async () => {
try {
setError(null);
if (!webrtcManager || !webrtcManager.ecdhKeyPair) {
throw new Error("No public key available for advertising");
}
await bluetoothManager.startAdvertising(
webrtcManager.ecdhKeyPair.publicKey,
"SecureBit Device"
);
} catch (error2) {
setError("Failed to start advertising: " + error2.message);
}
};
const stopAdvertising = async () => {
try {
await bluetoothManager.stopAdvertising();
} catch (error2) {
setError("Failed to stop advertising: " + error2.message);
}
};
const sendPublicKey = async (deviceId) => {
try {
setError(null);
if (!webrtcManager || !webrtcManager.ecdhKeyPair) {
throw new Error("No public key available for sending");
}
await bluetoothManager.sendPublicKey(
webrtcManager.ecdhKeyPair.publicKey,
deviceId
);
} catch (error2) {
setError("Failed to send public key: " + error2.message);
}
};
const clearLogs = () => {
setLogs([]);
};
const startAutoConnection = async (deviceId) => {
try {
setError(null);
await bluetoothManager.startAutoConnection(deviceId);
} catch (error2) {
setError("Failed to start auto connection: " + error2.message);
}
};
const startAutoConnectionAsResponder = async (deviceId) => {
try {
setError(null);
await bluetoothManager.startAutoConnectionAsResponder(deviceId);
} catch (error2) {
setError("Failed to start auto connection as responder: " + error2.message);
}
};
if (!isVisible) return null;
return React.createElement("div", {
className: "fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 p-4"
}, [
React.createElement("div", {
key: "modal",
className: "bg-gray-900 rounded-lg shadow-xl max-w-2xl w-full max-h-[90vh] overflow-hidden"
}, [
// Header
React.createElement("div", {
key: "header",
className: "flex items-center justify-between p-6 border-b border-gray-700"
}, [
React.createElement("div", {
key: "title",
className: "flex items-center space-x-3"
}, [
React.createElement("i", {
key: "icon",
className: "fas fa-bluetooth text-blue-400 text-xl"
}),
React.createElement("h2", {
key: "text",
className: "text-xl font-semibold text-white"
}, "Bluetooth Key Transfer")
]),
React.createElement("button", {
key: "close",
onClick: onClose,
className: "text-gray-400 hover:text-white transition-colors"
}, [
React.createElement("i", {
className: "fas fa-times text-xl"
})
])
]),
// Content
React.createElement("div", {
key: "content",
className: "p-6 space-y-6 overflow-y-auto max-h-[calc(90vh-200px)]"
}, [
// Status Section
React.createElement("div", {
key: "status",
className: "space-y-4"
}, [
React.createElement("h3", {
key: "title",
className: "text-lg font-medium text-white"
}, "Bluetooth Status"),
React.createElement("div", {
key: "indicators",
className: "grid grid-cols-2 gap-4"
}, [
React.createElement("div", {
key: "support",
className: "flex items-center space-x-2"
}, [
React.createElement("div", {
className: `w-3 h-3 rounded-full ${isSupported ? "bg-green-500" : "bg-red-500"}`
}),
React.createElement("span", {
className: "text-sm text-gray-300"
}, "Bluetooth Supported")
]),
React.createElement("div", {
key: "availability",
className: "flex items-center space-x-2"
}, [
React.createElement("div", {
className: `w-3 h-3 rounded-full ${isAvailable ? "bg-green-500" : "bg-red-500"}`
}),
React.createElement("span", {
className: "text-sm text-gray-300"
}, "Bluetooth Available")
])
])
]),
// Controls Section
React.createElement("div", {
key: "controls",
className: "space-y-4"
}, [
React.createElement("h3", {
key: "title",
className: "text-lg font-medium text-white"
}, "Key Exchange"),
React.createElement("div", {
key: "buttons",
className: "grid grid-cols-1 sm:grid-cols-2 gap-4"
}, [
// Scanning Controls
React.createElement("div", {
key: "scanning",
className: "space-y-2"
}, [
React.createElement("h4", {
key: "title",
className: "text-sm font-medium text-gray-300"
}, "Discover Devices"),
React.createElement("button", {
key: "scan",
onClick: isScanning ? stopScanning : startScanning,
disabled: !isSupported || !isAvailable,
className: `w-full px-4 py-2 rounded-lg font-medium transition-colors ${isScanning ? "bg-red-600 hover:bg-red-700 text-white" : "bg-blue-600 hover:bg-blue-700 text-white disabled:bg-gray-600 disabled:cursor-not-allowed"}`
}, [
React.createElement("i", {
key: "icon",
className: `fas ${isScanning ? "fa-stop" : "fa-search"} mr-2`
}),
isScanning ? "Stop Scanning" : "Start Scanning"
])
]),
// Advertising Controls
React.createElement("div", {
key: "advertising",
className: "space-y-2"
}, [
React.createElement("h4", {
key: "title",
className: "text-sm font-medium text-gray-300"
}, "Share Your Key"),
React.createElement("button", {
key: "advertise",
onClick: isAdvertising ? stopAdvertising : startAdvertising,
disabled: !isSupported || !isAvailable,
className: `w-full px-4 py-2 rounded-lg font-medium transition-colors ${isAdvertising ? "bg-red-600 hover:bg-red-700 text-white" : "bg-green-600 hover:bg-green-700 text-white disabled:bg-gray-600 disabled:cursor-not-allowed"}`
}, [
React.createElement("i", {
key: "icon",
className: `fas ${isAdvertising ? "fa-stop" : "fa-broadcast-tower"} mr-2`
}),
isAdvertising ? "Stop Sharing" : "Start Sharing"
])
])
])
]),
// Connected Devices
connectedDevices.length > 0 && React.createElement("div", {
key: "devices",
className: "space-y-4"
}, [
React.createElement("h3", {
key: "title",
className: "text-lg font-medium text-white"
}, "Connected Devices"),
React.createElement("div", {
key: "list",
className: "space-y-2"
}, connectedDevices.map(
(device) => React.createElement("div", {
key: device.id,
className: "flex items-center justify-between p-3 bg-gray-800 rounded-lg"
}, [
React.createElement("div", {
key: "info",
className: "flex items-center space-x-3"
}, [
React.createElement("i", {
key: "icon",
className: "fas fa-mobile-alt text-blue-400"
}),
React.createElement("span", {
key: "name",
className: "text-white"
}, device.name)
]),
React.createElement("div", {
key: "buttons",
className: "flex space-x-2"
}, [
React.createElement("button", {
key: "auto-connect",
onClick: () => startAutoConnection(device.id),
className: "px-3 py-1 bg-green-600 hover:bg-green-700 text-white text-sm rounded transition-colors"
}, "Auto Connect"),
React.createElement("button", {
key: "auto-respond",
onClick: () => startAutoConnectionAsResponder(device.id),
className: "px-3 py-1 bg-purple-600 hover:bg-purple-700 text-white text-sm rounded transition-colors"
}, "Auto Respond"),
React.createElement("button", {
key: "send",
onClick: () => sendPublicKey(device.id),
className: "px-3 py-1 bg-blue-600 hover:bg-blue-700 text-white text-sm rounded transition-colors"
}, "Send Key")
])
])
))
]),
// Error Display
error && React.createElement("div", {
key: "error",
className: "p-4 bg-red-900 border border-red-700 rounded-lg"
}, [
React.createElement("div", {
key: "header",
className: "flex items-center space-x-2 mb-2"
}, [
React.createElement("i", {
key: "icon",
className: "fas fa-exclamation-triangle text-red-400"
}),
React.createElement("h4", {
key: "title",
className: "text-red-400 font-medium"
}, "Error")
]),
React.createElement("p", {
key: "message",
className: "text-red-300 text-sm"
}, error)
]),
// Logs Section
React.createElement("div", {
key: "logs",
className: "space-y-4"
}, [
React.createElement("div", {
key: "header",
className: "flex items-center justify-between"
}, [
React.createElement("h3", {
key: "title",
className: "text-lg font-medium text-white"
}, "Activity Log"),
React.createElement("button", {
key: "clear",
onClick: clearLogs,
className: "text-sm text-gray-400 hover:text-white transition-colors"
}, "Clear")
]),
React.createElement(
"div",
{
key: "log-list",
className: "bg-gray-800 rounded-lg p-4 max-h-40 overflow-y-auto"
},
logs.length === 0 ? React.createElement("p", {
key: "empty",
className: "text-gray-400 text-sm text-center"
}, "No activity yet") : logs.map(
(log, index) => React.createElement("div", {
key: index,
className: "text-xs text-gray-300 mb-1"
}, [
React.createElement("span", {
key: "time",
className: "text-gray-500"
}, `[${log.timestamp}] `),
React.createElement("span", {
key: "message",
className: "text-gray-300"
}, log.message),
log.data && React.createElement("pre", {
key: "data",
className: "text-gray-400 mt-1 ml-4"
}, log.data)
])
)
)
])
]),
// Footer
React.createElement("div", {
key: "footer",
className: "flex items-center justify-between p-6 border-t border-gray-700"
}, [
React.createElement("div", {
key: "info",
className: "text-sm text-gray-400"
}, "Bluetooth key exchange provides secure device-to-device communication"),
React.createElement("button", {
key: "close-footer",
onClick: onClose,
className: "px-4 py-2 bg-gray-700 hover:bg-gray-600 text-white rounded-lg transition-colors"
}, "Close")
])
])
]);
};
if (typeof window !== "undefined") {
window.BluetoothKeyTransfer = BluetoothKeyTransfer;
}

View File

@@ -1,575 +0,0 @@
// src/components/ui/Header.jsx
var EnhancedMinimalHeader = ({
status,
fingerprint,
verificationCode,
onDisconnect,
isConnected,
securityLevel,
webrtcManager
}) => {
const [realSecurityLevel, setRealSecurityLevel] = React.useState(null);
const [lastSecurityUpdate, setLastSecurityUpdate] = React.useState(0);
React.useEffect(() => {
let isUpdating = false;
let lastUpdateAttempt = 0;
const updateRealSecurityStatus = async () => {
const now = Date.now();
if (now - lastUpdateAttempt < 1e4) {
return;
}
if (isUpdating) {
return;
}
isUpdating = true;
lastUpdateAttempt = now;
try {
if (!webrtcManager || !isConnected) {
return;
}
const activeWebrtcManager = webrtcManager;
let realSecurityData = null;
if (typeof activeWebrtcManager.getRealSecurityLevel === "function") {
realSecurityData = await activeWebrtcManager.getRealSecurityLevel();
} else if (typeof activeWebrtcManager.calculateAndReportSecurityLevel === "function") {
realSecurityData = await activeWebrtcManager.calculateAndReportSecurityLevel();
} else {
realSecurityData = await window.EnhancedSecureCryptoUtils.calculateSecurityLevel(activeWebrtcManager);
}
if (realSecurityData && realSecurityData.isRealData !== false) {
const currentScore = realSecurityLevel?.score || 0;
const newScore = realSecurityData.score || 0;
if (currentScore !== newScore || !realSecurityLevel) {
setRealSecurityLevel(realSecurityData);
setLastSecurityUpdate(now);
} else if (window.DEBUG_MODE) {
}
} else {
console.warn(" Security calculation returned invalid data");
}
} catch (error) {
console.error(" Error in real security calculation:", error);
} finally {
isUpdating = false;
}
};
if (isConnected) {
updateRealSecurityStatus();
if (!realSecurityLevel || realSecurityLevel.score < 50) {
const retryInterval = setInterval(() => {
if (!realSecurityLevel || realSecurityLevel.score < 50) {
updateRealSecurityStatus();
} else {
clearInterval(retryInterval);
}
}, 5e3);
setTimeout(() => clearInterval(retryInterval), 3e4);
}
}
const interval = setInterval(updateRealSecurityStatus, 3e4);
return () => clearInterval(interval);
}, [webrtcManager, isConnected]);
React.useEffect(() => {
const handleSecurityUpdate = (event) => {
setTimeout(() => {
setLastSecurityUpdate(0);
}, 100);
};
const handleRealSecurityCalculated = (event) => {
if (event.detail && event.detail.securityData) {
setRealSecurityLevel(event.detail.securityData);
setLastSecurityUpdate(Date.now());
}
};
document.addEventListener("security-level-updated", handleSecurityUpdate);
document.addEventListener("real-security-calculated", handleRealSecurityCalculated);
window.forceHeaderSecurityUpdate = (webrtcManager2) => {
if (webrtcManager2 && window.EnhancedSecureCryptoUtils) {
window.EnhancedSecureCryptoUtils.calculateSecurityLevel(webrtcManager2).then((securityData) => {
if (securityData && securityData.isRealData !== false) {
setRealSecurityLevel(securityData);
setLastSecurityUpdate(Date.now());
console.log("\u2705 Header security level force-updated");
}
}).catch((error) => {
console.error("\u274C Force update failed:", error);
});
} else {
setLastSecurityUpdate(0);
}
};
return () => {
document.removeEventListener("security-level-updated", handleSecurityUpdate);
document.removeEventListener("real-security-calculated", handleRealSecurityCalculated);
};
}, []);
React.useEffect(() => {
setHasActiveSession(true);
setCurrentTimeLeft(0);
setSessionType("premium");
}, []);
React.useEffect(() => {
setHasActiveSession(true);
setCurrentTimeLeft(0);
setSessionType("premium");
}, [sessionTimeLeft]);
React.useEffect(() => {
const handleForceUpdate = (event) => {
setHasActiveSession(true);
setCurrentTimeLeft(0);
setSessionType("premium");
};
const handleConnectionCleaned = () => {
if (window.DEBUG_MODE) {
console.log("\u{1F9F9} Connection cleaned - clearing security data in header");
}
setRealSecurityLevel(null);
setLastSecurityUpdate(0);
setHasActiveSession(false);
setCurrentTimeLeft(0);
setSessionType("unknown");
};
const handlePeerDisconnect = () => {
if (window.DEBUG_MODE) {
console.log("\u{1F44B} Peer disconnect detected - clearing security data in header");
}
setRealSecurityLevel(null);
setLastSecurityUpdate(0);
};
const handleDisconnected = () => {
setRealSecurityLevel(null);
setLastSecurityUpdate(0);
setHasActiveSession(false);
setCurrentTimeLeft(0);
setSessionType("unknown");
};
document.addEventListener("force-header-update", handleForceUpdate);
document.addEventListener("peer-disconnect", handlePeerDisconnect);
document.addEventListener("connection-cleaned", handleConnectionCleaned);
document.addEventListener("disconnected", handleDisconnected);
return () => {
document.removeEventListener("force-header-update", handleForceUpdate);
document.removeEventListener("peer-disconnect", handlePeerDisconnect);
document.removeEventListener("connection-cleaned", handleConnectionCleaned);
document.removeEventListener("disconnected", handleDisconnected);
};
}, []);
const handleSecurityClick = async (event) => {
if (event && (event.button === 2 || event.ctrlKey || event.metaKey)) {
if (onDisconnect && typeof onDisconnect === "function") {
onDisconnect();
return;
}
}
event.preventDefault();
event.stopPropagation();
let realTestResults = null;
if (webrtcManager && window.EnhancedSecureCryptoUtils) {
try {
realTestResults = await window.EnhancedSecureCryptoUtils.calculateSecurityLevel(webrtcManager);
console.log("\u2705 Real security tests completed:", realTestResults);
} catch (error) {
console.error("\u274C Real security tests failed:", error);
}
} else {
console.log("\u26A0\uFE0F Cannot run security tests:", {
webrtcManager: !!webrtcManager,
cryptoUtils: !!window.EnhancedSecureCryptoUtils
});
}
if (!realTestResults && !realSecurityLevel) {
alert("Security verification in progress...\nPlease wait for real-time cryptographic verification to complete.");
return;
}
let securityData = realTestResults || realSecurityLevel;
if (!securityData) {
securityData = {
level: "UNKNOWN",
score: 0,
color: "gray",
verificationResults: {},
timestamp: Date.now(),
details: "Security verification not available",
isRealData: false,
passedChecks: 0,
totalChecks: 0
};
console.log("Using fallback security data:", securityData);
}
let message = `REAL-TIME SECURITY VERIFICATION
`;
message += `Security Level: ${securityData.level} (${securityData.score}%)
`;
message += `Verification Time: ${new Date(securityData.timestamp).toLocaleTimeString()}
`;
message += `Data Source: ${securityData.isRealData ? "Real Cryptographic Tests" : "Simulated Data"}
`;
if (securityData.verificationResults) {
message += "DETAILED CRYPTOGRAPHIC TESTS:\n";
message += "=" + "=".repeat(40) + "\n";
const passedTests = Object.entries(securityData.verificationResults).filter(([key, result]) => result.passed);
const failedTests = Object.entries(securityData.verificationResults).filter(([key, result]) => !result.passed);
if (passedTests.length > 0) {
message += "PASSED TESTS:\n";
passedTests.forEach(([key, result]) => {
const testName = key.replace(/([A-Z])/g, " $1").replace(/^./, (str) => str.toUpperCase());
message += ` ${testName}: ${result.details || "Test passed"}
`;
});
message += "\n";
}
if (failedTests.length > 0) {
message += "FAILED/UNAVAILABLE TESTS:\n";
failedTests.forEach(([key, result]) => {
const testName = key.replace(/([A-Z])/g, " $1").replace(/^./, (str) => str.toUpperCase());
message += ` ${testName}: ${result.details || "Test failed or unavailable"}
`;
});
message += "\n";
}
message += `SUMMARY:
`;
message += `Passed: ${securityData.passedChecks}/${securityData.totalChecks} tests
`;
message += `Score: ${securityData.score}/${securityData.maxPossibleScore || 100} points
`;
}
message += `SECURITY FEATURES STATUS:
`;
message += "=" + "=".repeat(40) + "\n";
if (securityData.verificationResults) {
const features = {
"ECDSA Digital Signatures": securityData.verificationResults.verifyECDSASignatures?.passed || false,
"ECDH Key Exchange": securityData.verificationResults.verifyECDHKeyExchange?.passed || false,
"AES-GCM Encryption": securityData.verificationResults.verifyEncryption?.passed || false,
"Message Integrity (HMAC)": securityData.verificationResults.verifyMessageIntegrity?.passed || false,
"Perfect Forward Secrecy": securityData.verificationResults.verifyPerfectForwardSecrecy?.passed || false,
"Replay Protection": securityData.verificationResults.verifyReplayProtection?.passed || false,
"DTLS Fingerprint": securityData.verificationResults.verifyDTLSFingerprint?.passed || false,
"SAS Verification": securityData.verificationResults.verifySASVerification?.passed || false,
"Metadata Protection": securityData.verificationResults.verifyMetadataProtection?.passed || false,
"Traffic Obfuscation": securityData.verificationResults.verifyTrafficObfuscation?.passed || false
};
Object.entries(features).forEach(([feature, isEnabled]) => {
message += `${isEnabled ? "\u2705" : "\u274C"} ${feature}
`;
});
} else {
message += `\u2705 ECDSA Digital Signatures
`;
message += `\u2705 ECDH Key Exchange
`;
message += `\u2705 AES-GCM Encryption
`;
message += `\u2705 Message Integrity (HMAC)
`;
message += `\u2705 Perfect Forward Secrecy
`;
message += `\u2705 Replay Protection
`;
message += `\u2705 DTLS Fingerprint
`;
message += `\u2705 SAS Verification
`;
message += `\u2705 Metadata Protection
`;
message += `\u2705 Traffic Obfuscation
`;
}
message += `
${securityData.details || "Real cryptographic verification completed"}`;
if (securityData.isRealData) {
message += "\n\n\u2705 This is REAL-TIME verification using actual cryptographic functions.";
} else {
message += "\n\n\u26A0\uFE0F Warning: This data may be simulated. Connection may not be fully established.";
}
const modal = document.createElement("div");
modal.style.cssText = `
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0,0,0,0.8);
z-index: 10000;
display: flex;
align-items: center;
justify-content: center;
font-family: monospace;
`;
const content = document.createElement("div");
content.style.cssText = `
background: #1a1a1a;
color: #fff;
padding: 20px;
border-radius: 8px;
max-width: 80%;
max-height: 80%;
overflow-y: auto;
white-space: pre-line;
border: 1px solid #333;
`;
content.textContent = message;
modal.appendChild(content);
modal.addEventListener("click", (e) => {
if (e.target === modal) {
document.body.removeChild(modal);
}
});
const handleKeyDown = (e) => {
if (e.key === "Escape") {
document.body.removeChild(modal);
document.removeEventListener("keydown", handleKeyDown);
}
};
document.addEventListener("keydown", handleKeyDown);
document.body.appendChild(modal);
};
const getStatusConfig = () => {
switch (status) {
case "connected":
return {
text: "Connected",
className: "status-connected",
badgeClass: "bg-green-500/10 text-green-400 border-green-500/20"
};
case "verifying":
return {
text: "Verifying...",
className: "status-verifying",
badgeClass: "bg-purple-500/10 text-purple-400 border-purple-500/20"
};
case "connecting":
return {
text: "Connecting...",
className: "status-connecting",
badgeClass: "bg-blue-500/10 text-blue-400 border-blue-500/20"
};
case "retrying":
return {
text: "Retrying...",
className: "status-connecting",
badgeClass: "bg-yellow-500/10 text-yellow-400 border-yellow-500/20"
};
case "failed":
return {
text: "Error",
className: "status-failed",
badgeClass: "bg-red-500/10 text-red-400 border-red-500/20"
};
case "reconnecting":
return {
text: "Reconnecting...",
className: "status-connecting",
badgeClass: "bg-yellow-500/10 text-yellow-400 border-yellow-500/20"
};
case "peer_disconnected":
return {
text: "Peer disconnected",
className: "status-failed",
badgeClass: "bg-orange-500/10 text-orange-400 border-orange-500/20"
};
default:
return {
text: "Not connected",
className: "status-disconnected",
badgeClass: "bg-gray-500/10 text-gray-400 border-gray-500/20"
};
}
};
const config = getStatusConfig();
const displaySecurityLevel = isConnected ? realSecurityLevel || securityLevel : null;
const getSecurityIndicatorDetails = () => {
if (!displaySecurityLevel) {
return {
tooltip: "Security verification in progress...",
isVerified: false,
dataSource: "loading"
};
}
const isRealData = displaySecurityLevel.isRealData !== false;
const baseTooltip = `${displaySecurityLevel.level} (${displaySecurityLevel.score}%)`;
if (isRealData) {
return {
tooltip: `${baseTooltip} - Real-time verification \u2705
Right-click or Ctrl+click to disconnect`,
isVerified: true,
dataSource: "real"
};
} else {
return {
tooltip: `${baseTooltip} - Estimated (connection establishing...)
Right-click or Ctrl+click to disconnect`,
isVerified: false,
dataSource: "estimated"
};
}
};
const securityDetails = getSecurityIndicatorDetails();
React.useEffect(() => {
window.debugHeaderSecurity = () => {
console.log("\u{1F50D} Header Security Debug:", {
realSecurityLevel,
lastSecurityUpdate,
isConnected,
webrtcManagerProp: !!webrtcManager,
windowWebrtcManager: !!window.webrtcManager,
cryptoUtils: !!window.EnhancedSecureCryptoUtils,
displaySecurityLevel,
securityDetails
});
};
return () => {
delete window.debugHeaderSecurity;
};
}, [realSecurityLevel, lastSecurityUpdate, isConnected, webrtcManager, displaySecurityLevel, securityDetails]);
return React.createElement("header", {
className: "header-minimal sticky top-0 z-50"
}, [
React.createElement("div", {
key: "container",
className: "max-w-7xl mx-auto px-4 sm:px-6 lg:px-8"
}, [
React.createElement("div", {
key: "content",
className: "flex items-center justify-between h-16"
}, [
// Logo and Title
React.createElement("div", {
key: "logo-section",
className: "flex items-center space-x-2 sm:space-x-3"
}, [
React.createElement("div", {
key: "logo",
className: "icon-container w-8 h-8 sm:w-10 sm:h-10"
}, [
React.createElement("i", {
className: "fas fa-shield-halved accent-orange text-sm sm:text-base"
})
]),
React.createElement("div", {
key: "title-section"
}, [
React.createElement("h1", {
key: "title",
className: "text-lg sm:text-xl font-semibold text-primary"
}, "SecureBit.chat"),
React.createElement("p", {
key: "subtitle",
className: "text-xs sm:text-sm text-muted hidden sm:block"
}, "End-to-end freedom v4.3.120")
])
]),
// Status and Controls - Responsive
React.createElement("div", {
key: "status-section",
className: "flex items-center space-x-2 sm:space-x-3"
}, [
displaySecurityLevel && React.createElement("div", {
key: "security-level",
className: "hidden md:flex items-center space-x-2 cursor-pointer hover:opacity-80 transition-opacity duration-200",
onClick: handleSecurityClick,
onContextMenu: (e) => {
e.preventDefault();
if (onDisconnect && typeof onDisconnect === "function") {
onDisconnect();
}
},
title: securityDetails.tooltip
}, [
React.createElement("div", {
key: "security-icon",
className: `w-6 h-6 rounded-full flex items-center justify-center relative ${displaySecurityLevel.color === "green" ? "bg-green-500/20" : displaySecurityLevel.color === "orange" ? "bg-orange-500/20" : displaySecurityLevel.color === "yellow" ? "bg-yellow-500/20" : "bg-red-500/20"} ${securityDetails.isVerified ? "" : "animate-pulse"}`
}, [
React.createElement("i", {
className: `fas fa-shield-alt text-xs ${displaySecurityLevel.color === "green" ? "text-green-400" : displaySecurityLevel.color === "orange" ? "text-orange-400" : displaySecurityLevel.color === "yellow" ? "text-yellow-400" : "text-red-400"}`
})
]),
React.createElement("div", {
key: "security-info",
className: "flex flex-col"
}, [
React.createElement("div", {
key: "security-level-text",
className: "text-xs font-medium text-primary flex items-center space-x-1"
}, [
React.createElement("span", {}, `${displaySecurityLevel.level} (${displaySecurityLevel.score}%)`)
]),
React.createElement(
"div",
{
key: "security-details",
className: "text-xs text-muted mt-1 hidden lg:block"
},
securityDetails.dataSource === "real" ? `${displaySecurityLevel.passedChecks || 0}/${displaySecurityLevel.totalChecks || 0} tests` : displaySecurityLevel.details || `Stage ${displaySecurityLevel.stage || 1}`
),
React.createElement("div", {
key: "security-progress",
className: "w-16 h-1 bg-gray-600 rounded-full overflow-hidden"
}, [
React.createElement("div", {
key: "progress-bar",
className: `h-full transition-all duration-500 ${displaySecurityLevel.color === "green" ? "bg-green-400" : displaySecurityLevel.color === "orange" ? "bg-orange-400" : displaySecurityLevel.color === "yellow" ? "bg-yellow-400" : "bg-red-400"}`,
style: { width: `${displaySecurityLevel.score}%` }
})
])
])
]),
// Mobile Security Indicator
displaySecurityLevel && React.createElement("div", {
key: "mobile-security",
className: "md:hidden flex items-center"
}, [
React.createElement("div", {
key: "mobile-security-icon",
className: `w-8 h-8 rounded-full flex items-center justify-center cursor-pointer hover:opacity-80 transition-opacity duration-200 relative ${displaySecurityLevel.color === "green" ? "bg-green-500/20" : displaySecurityLevel.color === "orange" ? "bg-orange-500/20" : displaySecurityLevel.color === "yellow" ? "bg-yellow-500/20" : "bg-red-500/20"} ${securityDetails.isVerified ? "" : "animate-pulse"}`,
title: securityDetails.tooltip,
onClick: handleSecurityClick,
onContextMenu: (e) => {
e.preventDefault();
if (onDisconnect && typeof onDisconnect === "function") {
onDisconnect();
}
}
}, [
React.createElement("i", {
className: `fas fa-shield-alt text-sm ${displaySecurityLevel.color === "green" ? "text-green-400" : displaySecurityLevel.color === "orange" ? "text-orange-400" : displaySecurityLevel.color === "yellow" ? "text-yellow-400" : "text-red-400"}`
})
])
]),
// Status Badge
React.createElement("div", {
key: "status-badge",
className: `px-2 sm:px-3 py-1.5 rounded-lg border ${config.badgeClass} flex items-center space-x-1 sm:space-x-2`
}, [
React.createElement("span", {
key: "status-dot",
className: `status-dot ${config.className}`
}),
React.createElement("span", {
key: "status-text",
className: "text-xs sm:text-sm font-medium"
}, config.text)
]),
// Disconnect Button
isConnected && React.createElement("button", {
key: "disconnect-btn",
onClick: onDisconnect,
className: "p-1.5 sm:px-3 sm:py-1.5 bg-red-500/10 hover:bg-red-500/20 text-red-400 border border-red-500/20 rounded-lg transition-all duration-200 text-sm"
}, [
React.createElement("i", {
className: "fas fa-power-off sm:mr-2"
}),
React.createElement("span", {
className: "hidden sm:inline"
}, "Disconnect")
])
])
])
])
]);
};
window.EnhancedMinimalHeader = EnhancedMinimalHeader;