175 lines
5.4 KiB
JavaScript
175 lines
5.4 KiB
JavaScript
// Vault1984 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, '&').replace(/</g, '<').replace(/>/g, '>');
|
|
}
|
|
|
|
// Initialize
|
|
loadMatches();
|