clawvault/extension/popup.js

175 lines
5.4 KiB
JavaScript

// ClawVault Popup Script
let currentUrl = '';
let currentMatches = [];
// Get current tab URL
async function getCurrentUrl() {
const tabs = await chrome.tabs.query({ active: true, currentWindow: true });
if (tabs[0]) {
currentUrl = tabs[0].url;
document.getElementById('currentUrl').textContent = new URL(currentUrl).hostname;
return currentUrl;
}
return '';
}
// Load matching credentials
async function loadMatches() {
const url = await getCurrentUrl();
if (!url) return;
const matchesDiv = document.getElementById('matches');
matchesDiv.innerHTML = '<div class="empty">Loading...</div>';
chrome.runtime.sendMessage({ action: 'getMatches', url }, (response) => {
if (chrome.runtime.lastError || !response || !response.success) {
matchesDiv.innerHTML = '<div class="empty">Not connected to vault. Check settings.</div>';
return;
}
currentMatches = response.matches;
if (!currentMatches || currentMatches.length === 0) {
matchesDiv.innerHTML = '<div class="empty">No matching credentials</div>';
return;
}
let html = '';
currentMatches.forEach((entry, idx) => {
const hasL2 = entry.data && entry.data.fields && entry.data.fields.some(f => f.l2);
html += `<div class="entry" data-idx="${idx}">
<div class="entry-title">${escapeHtml(entry.title)}</div>
<div class="entry-meta">
${entry.type}
${hasL2 ? '<span class="entry-l2">🔒 L2</span>' : ''}
</div>
</div>`;
});
matchesDiv.innerHTML = html;
// Add click handlers
document.querySelectorAll('.entry').forEach(el => {
el.addEventListener('click', () => fillEntry(parseInt(el.dataset.idx)));
});
});
}
// Fill an entry into the page
async function fillEntry(idx) {
const entry = currentMatches[idx];
if (!entry || !entry.data || !entry.data.fields) return;
// Get form fields from page
chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => {
chrome.tabs.sendMessage(tabs[0].id, { action: 'getFormFields' }, (response) => {
if (chrome.runtime.lastError || !response || !response.success) {
// Fallback: try simple fill
simpleFill(entry);
return;
}
// Request LLM mapping
chrome.runtime.sendMessage({
action: 'mapFields',
data: {
entry_id: entry.entry_id,
page_fields: response.fields
}
}, (mapResponse) => {
if (mapResponse && mapResponse.success && mapResponse.mapping) {
// Convert mapping to actual values
const fields = {};
Object.entries(mapResponse.mapping).forEach(([label, selector]) => {
const field = entry.data.fields.find(f => f.label === label);
if (field && !field.l2) {
fields[selector] = field.value;
}
});
chrome.runtime.sendMessage({ action: 'fill', fields });
} else {
simpleFill(entry);
}
});
});
});
window.close();
}
// Simple fill without LLM mapping
function simpleFill(entry) {
const fields = {};
entry.data.fields.forEach(f => {
if (f.l2) return;
if (f.label.toLowerCase().includes('user') || f.label.toLowerCase().includes('email')) {
fields['input[type="email"], input[type="text"], input[name*="user"], input[name*="email"]'] = f.value;
}
if (f.label.toLowerCase().includes('password') || f.kind === 'password') {
fields['input[type="password"]'] = f.value;
}
});
chrome.runtime.sendMessage({ action: 'fill', fields });
}
// Settings handlers
document.getElementById('settingsLink').addEventListener('click', (e) => {
e.preventDefault();
document.getElementById('matchView').style.display = 'none';
document.getElementById('settingsView').classList.add('active');
loadSettings();
});
document.getElementById('backToMatches').addEventListener('click', () => {
document.getElementById('settingsView').classList.remove('active');
document.getElementById('matchView').style.display = 'block';
});
document.getElementById('saveSettings').addEventListener('click', saveSettings);
async function loadSettings() {
chrome.runtime.sendMessage({ action: 'getSettings' }, (response) => {
if (response && response.success) {
document.getElementById('vaultUrl').value = response.settings.vaultUrl || '';
document.getElementById('apiToken').value = response.settings.apiToken || '';
}
});
}
async function saveSettings() {
const vaultUrl = document.getElementById('vaultUrl').value.trim();
const apiToken = document.getElementById('apiToken').value.trim();
chrome.runtime.sendMessage({
action: 'saveSettings',
vaultUrl,
apiToken
}, (response) => {
const status = document.getElementById('settingsStatus');
if (response && response.success) {
status.className = 'status success';
status.textContent = 'Settings saved!';
setTimeout(() => {
document.getElementById('settingsView').classList.remove('active');
document.getElementById('matchView').style.display = 'block';
loadMatches();
}, 1000);
} else {
status.className = 'status error';
status.textContent = 'Failed to save settings';
}
});
}
function escapeHtml(str) {
if (!str) return '';
return str.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
}
// Initialize
loadMatches();