unlock.ts raw

   1  import browser from 'webextension-polyfill';
   2  
   3  export interface UnlockRequestMessage {
   4    type: 'unlock-request';
   5    id: string;
   6    password: string;
   7  }
   8  
   9  export interface UnlockResponseMessage {
  10    type: 'unlock-response';
  11    id: string;
  12    success: boolean;
  13    error?: string;
  14  }
  15  
  16  const params = new URLSearchParams(location.search);
  17  const id = params.get('id') as string;
  18  const host = params.get('host');
  19  
  20  // Elements
  21  const passwordInput = document.getElementById('passwordInput') as HTMLInputElement;
  22  const togglePasswordBtn = document.getElementById('togglePassword');
  23  const unlockBtn = document.getElementById('unlockBtn') as HTMLButtonElement;
  24  const derivingOverlay = document.getElementById('derivingOverlay');
  25  const errorAlert = document.getElementById('errorAlert');
  26  const errorMessage = document.getElementById('errorMessage');
  27  const hostInfo = document.getElementById('hostInfo');
  28  const hostSpan = document.getElementById('hostSpan');
  29  
  30  // Show host info if available
  31  if (host && hostInfo && hostSpan) {
  32    hostSpan.innerText = host;
  33    hostInfo.classList.remove('hidden');
  34  }
  35  
  36  // Toggle password visibility
  37  togglePasswordBtn?.addEventListener('click', () => {
  38    if (passwordInput.type === 'password') {
  39      passwordInput.type = 'text';
  40      togglePasswordBtn.innerHTML = '<i class="bi bi-eye-slash"></i>';
  41    } else {
  42      passwordInput.type = 'password';
  43      togglePasswordBtn.innerHTML = '<i class="bi bi-eye"></i>';
  44    }
  45  });
  46  
  47  // Enable/disable unlock button based on password input
  48  passwordInput?.addEventListener('input', () => {
  49    unlockBtn.disabled = !passwordInput.value;
  50  });
  51  
  52  // Handle enter key
  53  passwordInput?.addEventListener('keyup', (e) => {
  54    if (e.key === 'Enter' && passwordInput.value) {
  55      attemptUnlock();
  56    }
  57  });
  58  
  59  // Handle unlock button click
  60  unlockBtn?.addEventListener('click', attemptUnlock);
  61  
  62  async function attemptUnlock() {
  63    if (!passwordInput?.value) return;
  64  
  65    // Show deriving overlay
  66    derivingOverlay?.classList.remove('hidden');
  67    errorAlert?.classList.add('hidden');
  68  
  69    const message: UnlockRequestMessage = {
  70      type: 'unlock-request',
  71      id,
  72      password: passwordInput.value,
  73    };
  74  
  75    try {
  76      const response = await browser.runtime.sendMessage(message) as UnlockResponseMessage;
  77  
  78      if (response.success) {
  79        // Success - close the window
  80        window.close();
  81      } else {
  82        // Failed - show error
  83        derivingOverlay?.classList.add('hidden');
  84        showError(response.error || 'Invalid password');
  85      }
  86    } catch (error) {
  87      console.error('Failed to send unlock message:', error);
  88      derivingOverlay?.classList.add('hidden');
  89      showError('Failed to unlock vault');
  90    }
  91  }
  92  
  93  function showError(message: string) {
  94    if (errorAlert && errorMessage) {
  95      errorMessage.innerText = message;
  96      errorAlert.classList.remove('hidden');
  97      setTimeout(() => {
  98        errorAlert.classList.add('hidden');
  99      }, 3000);
 100    }
 101  }
 102  
 103  // Focus password input on load
 104  document.addEventListener('DOMContentLoaded', () => {
 105    passwordInput?.focus();
 106  });
 107