- Add unlock popup window that appears when vault is locked and a NIP-07 request is made (similar to permission prompt popup) - Implement standalone vault unlock logic in background script using Argon2id key derivation and AES-GCM decryption - Queue pending NIP-07 requests while waiting for unlock, process after success - Add unlock.html and unlock.ts for both Chrome and Firefox extensions Files modified: - package.json (version bump to v1.0.10) - projects/chrome/public/unlock.html (new) - projects/chrome/src/unlock.ts (new) - projects/chrome/src/background.ts - projects/chrome/src/background-common.ts - projects/chrome/custom-webpack.config.ts - projects/chrome/tsconfig.app.json - projects/firefox/public/unlock.html (new) - projects/firefox/src/unlock.ts (new) - projects/firefox/src/background.ts - projects/firefox/src/background-common.ts - projects/firefox/custom-webpack.config.ts - projects/firefox/tsconfig.app.json 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
107 lines
2.9 KiB
TypeScript
107 lines
2.9 KiB
TypeScript
import browser from 'webextension-polyfill';
|
|
|
|
export interface UnlockRequestMessage {
|
|
type: 'unlock-request';
|
|
id: string;
|
|
password: string;
|
|
}
|
|
|
|
export interface UnlockResponseMessage {
|
|
type: 'unlock-response';
|
|
id: string;
|
|
success: boolean;
|
|
error?: string;
|
|
}
|
|
|
|
const params = new URLSearchParams(location.search);
|
|
const id = params.get('id') as string;
|
|
const host = params.get('host');
|
|
|
|
// Elements
|
|
const passwordInput = document.getElementById('passwordInput') as HTMLInputElement;
|
|
const togglePasswordBtn = document.getElementById('togglePassword');
|
|
const unlockBtn = document.getElementById('unlockBtn') as HTMLButtonElement;
|
|
const derivingOverlay = document.getElementById('derivingOverlay');
|
|
const errorAlert = document.getElementById('errorAlert');
|
|
const errorMessage = document.getElementById('errorMessage');
|
|
const hostInfo = document.getElementById('hostInfo');
|
|
const hostSpan = document.getElementById('hostSpan');
|
|
|
|
// Show host info if available
|
|
if (host && hostInfo && hostSpan) {
|
|
hostSpan.innerText = host;
|
|
hostInfo.classList.remove('hidden');
|
|
}
|
|
|
|
// Toggle password visibility
|
|
togglePasswordBtn?.addEventListener('click', () => {
|
|
if (passwordInput.type === 'password') {
|
|
passwordInput.type = 'text';
|
|
togglePasswordBtn.innerHTML = '<i class="bi bi-eye-slash"></i>';
|
|
} else {
|
|
passwordInput.type = 'password';
|
|
togglePasswordBtn.innerHTML = '<i class="bi bi-eye"></i>';
|
|
}
|
|
});
|
|
|
|
// Enable/disable unlock button based on password input
|
|
passwordInput?.addEventListener('input', () => {
|
|
unlockBtn.disabled = !passwordInput.value;
|
|
});
|
|
|
|
// Handle enter key
|
|
passwordInput?.addEventListener('keyup', (e) => {
|
|
if (e.key === 'Enter' && passwordInput.value) {
|
|
attemptUnlock();
|
|
}
|
|
});
|
|
|
|
// Handle unlock button click
|
|
unlockBtn?.addEventListener('click', attemptUnlock);
|
|
|
|
async function attemptUnlock() {
|
|
if (!passwordInput?.value) return;
|
|
|
|
// Show deriving overlay
|
|
derivingOverlay?.classList.remove('hidden');
|
|
errorAlert?.classList.add('hidden');
|
|
|
|
const message: UnlockRequestMessage = {
|
|
type: 'unlock-request',
|
|
id,
|
|
password: passwordInput.value,
|
|
};
|
|
|
|
try {
|
|
const response = await browser.runtime.sendMessage(message) as UnlockResponseMessage;
|
|
|
|
if (response.success) {
|
|
// Success - close the window
|
|
window.close();
|
|
} else {
|
|
// Failed - show error
|
|
derivingOverlay?.classList.add('hidden');
|
|
showError(response.error || 'Invalid password');
|
|
}
|
|
} catch (error) {
|
|
console.error('Failed to send unlock message:', error);
|
|
derivingOverlay?.classList.add('hidden');
|
|
showError('Failed to unlock vault');
|
|
}
|
|
}
|
|
|
|
function showError(message: string) {
|
|
if (errorAlert && errorMessage) {
|
|
errorMessage.innerText = message;
|
|
errorAlert.classList.remove('hidden');
|
|
setTimeout(() => {
|
|
errorAlert.classList.add('hidden');
|
|
}, 3000);
|
|
}
|
|
}
|
|
|
|
// Focus password input on load
|
|
document.addEventListener('DOMContentLoaded', () => {
|
|
passwordInput?.focus();
|
|
});
|