import React, { useState, useRef } from 'react'; import { X, Upload, Download, FileJson, Check, Copy, AlertCircle, ArrowRight, ShieldAlert, KeyRound, FileText, CreditCard, User } from 'lucide-react'; import { useVault } from '../context/VaultContext.js'; import { parseBitwardenExport } from '@2password/core'; export const BitwardenModal: React.FC = () => { const { isBitwardenModalOpen, closeBitwardenModal, importBitwarden, exportBitwarden, activeItems } = useVault(); const [activeTab, setActiveTab] = useState<'import' | 'export'>('import'); const [jsonInput, setJsonInput] = useState(''); const [preview, setPreview] = useState<{ total: number; logins: number; notes: number; cards: number; identities: number; } | null>(null); const [parseError, setParseError] = useState(null); const [importSuccess, setImportSuccess] = useState(null); const [isImporting, setIsImporting] = useState(false); const fileInputRef = useRef(null); const [exportJson, setExportJson] = useState(null); const [copiedExport, setCopiedExport] = useState(false); const [isExporting, setIsExporting] = useState(false); if (!isBitwardenModalOpen) return null; const handleJsonChange = (text: string) => { setJsonInput(text); setParseError(null); setImportSuccess(null); if (!text.trim()) { setPreview(null); return; } try { const { items } = parseBitwardenExport(text); setPreview({ total: items.length, logins: items.filter((i) => i.type === 'login').length, notes: items.filter((i) => i.type === 'secureNote').length, cards: items.filter((i) => i.type === 'card').length, identities: items.filter((i) => i.type === 'identity').length }); } catch (err: any) { setPreview(null); setParseError(err.message || 'Invalid Bitwarden JSON format.'); } }; const handleFileUpload = (e: React.ChangeEvent) => { const file = e.target.files?.[0]; if (!file) return; const reader = new FileReader(); reader.onload = (event) => { const content = event.target?.result as string; handleJsonChange(content); }; reader.readAsText(file); }; const handleExecuteImport = async () => { if (!jsonInput.trim()) return; setIsImporting(true); setParseError(null); try { const res = await importBitwarden(jsonInput); setImportSuccess(res.importedCount); setJsonInput(''); setPreview(null); } catch (err: any) { setParseError(err.message || 'Import failed.'); } finally { setIsImporting(false); } }; const handleGenerateExport = async () => { setIsExporting(true); try { const json = await exportBitwarden(); setExportJson(json); } catch (err: any) { alert(err.message || 'Failed to export vault.'); } finally { setIsExporting(false); } }; const handleDownloadFile = () => { if (!exportJson) return; const blob = new Blob([exportJson], { type: 'application/json' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = `bitwarden_export_${new Date().toISOString().slice(0, 10)}.json`; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url); }; const handleCopyExport = async () => { if (!exportJson) return; try { await navigator.clipboard.writeText(exportJson); setCopiedExport(true); setTimeout(() => setCopiedExport(false), 2000); } catch { // ignore } }; return (

Bitwarden Vault Sync

Import & Export Standard JSON

{activeTab === 'import' ? (
Export your Bitwarden vault as unencrypted .json format. Upload the file or paste the contents below.
{importSuccess !== null && (
Successfully imported {importSuccess} credentials into your encrypted vault!
)} {parseError && (
{parseError}
)}
fileInputRef.current?.click()} className="p-6 rounded-2xl border-2 border-dashed border-slate-700 hover:border-teal-400/60 bg-slate-950/40 hover:bg-slate-950/80 cursor-pointer transition-all text-center group" >

Choose or drag & drop a Bitwarden .json export file

Supports unencrypted JSON exports