verstak/extension/popup/popup.js

215 lines
6.7 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Verstak Bridge — Popup Script
// Shows connection status, queue count, recent events, and settings.
const STORAGE_KEY = 'verstak_queue';
const BRIDGE_KEY = 'verstak_bridge_config';
const RECENT_KEY = 'verstak_recent';
const DEFAULT_PORT = 9786;
document.addEventListener('DOMContentLoaded', () => {
updateUI();
restoreToggleState();
restoreSettingsPanel();
document.getElementById('sync-btn').addEventListener('click', forceSync);
document.getElementById('track-toggle').addEventListener('change', onToggle);
document.getElementById('settings-btn').addEventListener('click', toggleSettings);
document.getElementById('save-port-btn').addEventListener('click', savePort);
document.getElementById('test-port-btn').addEventListener('click', testPort);
});
function updateUI() {
chrome.storage.local.get([STORAGE_KEY, BRIDGE_KEY, RECENT_KEY], (data) => {
const queue = data[STORAGE_KEY] || [];
const config = data[BRIDGE_KEY] || {};
const recent = data[RECENT_KEY] || [];
// Queue count
document.getElementById('queue-count').textContent = queue.length;
// Bridge status
const statusEl = document.getElementById('bridge-status');
const portEl = document.getElementById('bridge-port');
const dotEl = document.getElementById('status-dot');
if (config.bridgeReachable) {
statusEl.textContent = 'Доступен';
statusEl.className = 'value online';
dotEl.className = 'dot online';
portEl.textContent = '127.0.0.1:' + (config.port || DEFAULT_PORT);
} else {
statusEl.textContent = 'Недоступен';
statusEl.className = 'value offline';
dotEl.className = 'dot offline';
portEl.textContent = config.lastPing ? '—' : 'проверка...';
}
// Update settings panel port field
const portInput = document.getElementById('port-input');
if (portInput && !portInput.dataset.userEdited) {
portInput.value = config.port || DEFAULT_PORT;
}
// Recent events
renderRecent(recent, config.bridgeReachable);
});
}
function renderRecent(events, reachable) {
const list = document.getElementById('event-list');
list.innerHTML = '';
if (!events || events.length === 0) {
list.innerHTML = '<p class="empty">Нет событий</p>';
return;
}
const lastFive = events.slice(-5).reverse();
for (const ev of lastFive) {
const item = document.createElement('div');
item.className = 'event-item';
item.innerHTML = `
<div class="domain">${escapeHtml(ev.domain || '?')}</div>
<div class="title">${escapeHtml(truncate(ev.title || ev.url, 60))}</div>
<div class="duration">${ev.active_seconds || 0}с${reachable ? '' : ' (ожидает)'}</div>
`;
list.appendChild(item);
}
}
function forceSync() {
const btn = document.getElementById('sync-btn');
btn.disabled = true;
btn.textContent = '⏳ Отправка...';
chrome.runtime.sendMessage({ type: 'FORCE_FLUSH' }, () => {
setTimeout(() => {
updateUI();
btn.disabled = false;
btn.textContent = '🔄 Отправить сейчас';
}, 1000);
});
}
function onToggle(e) {
const enabled = e.target.checked;
chrome.storage.local.set({ 'verstak_tracking_enabled': enabled });
chrome.runtime.sendMessage({ type: 'SET_TRACKING', enabled });
}
function restoreToggleState() {
chrome.storage.local.get('verstak_tracking_enabled', (data) => {
const enabled = data.verstak_tracking_enabled !== false;
document.getElementById('track-toggle').checked = enabled;
});
}
// --- Settings panel ---
function toggleSettings() {
const panel = document.getElementById('settings-panel');
const main = document.getElementById('main-panel');
const btn = document.getElementById('settings-btn');
if (panel.style.display === 'none' || !panel.style.display) {
panel.style.display = 'block';
main.style.display = 'none';
btn.textContent = '← Назад';
} else {
panel.style.display = 'none';
main.style.display = 'block';
btn.textContent = '⚙';
}
}
function restoreSettingsPanel() {
chrome.storage.local.get(BRIDGE_KEY, (data) => {
const config = data[BRIDGE_KEY] || {};
const portInput = document.getElementById('port-input');
if (portInput) {
portInput.value = config.port || DEFAULT_PORT;
}
});
}
function savePort() {
const portInput = document.getElementById('port-input');
const port = parseInt(portInput.value, 10);
const statusEl = document.getElementById('port-status');
if (isNaN(port) || port < 1024 || port > 65535) {
statusEl.textContent = 'Порт: 102465535';
statusEl.className = 'port-status error';
return;
}
chrome.storage.local.get(BRIDGE_KEY, (data) => {
const config = data[BRIDGE_KEY] || {};
config.port = port;
chrome.storage.local.set({ [BRIDGE_KEY]: config }, () => {
statusEl.textContent = 'Сохранено';
statusEl.className = 'port-status ok';
// Trigger immediate ping with new port
chrome.runtime.sendMessage({ type: 'FORCE_FLUSH' });
setTimeout(() => { updateUI(); statusEl.textContent = ''; }, 1500);
});
});
}
function testPort() {
const portInput = document.getElementById('port-input');
const port = parseInt(portInput.value, 10);
const statusEl = document.getElementById('port-status');
if (isNaN(port) || port < 1024 || port > 65535) {
statusEl.textContent = 'Порт: 102465535';
statusEl.className = 'port-status error';
return;
}
statusEl.textContent = 'Проверка...';
statusEl.className = 'port-status';
fetch(`http://127.0.0.1:${port}/api/ping`, { signal: AbortSignal.timeout(3000) })
.then((res) => {
if (res.ok) {
statusEl.textContent = '✓ Сервер отвечает';
statusEl.className = 'port-status ok';
} else {
statusEl.textContent = `✗ Ошибка ${res.status}`;
statusEl.className = 'port-status error';
}
})
.catch((err) => {
if (err.name === 'TimeoutError' || err.name === 'AbortError') {
statusEl.textContent = '✗ Нет ответа (таймаут). Возможно, порт занят другим приложением или Verstak не запущен';
} else {
statusEl.textContent = '✗ Сервер недоступен';
}
statusEl.className = 'port-status error';
});
}
// Listen for updates from background
chrome.runtime.onMessage.addListener((msg) => {
if (msg.type === 'UI_UPDATE') {
updateUI();
}
});
// --- Helpers ---
function truncate(s, n) {
if (!s) return '';
return s.length > n ? s.substring(0, n) + '...' : s;
}
function escapeHtml(s) {
if (!s) return '';
return s
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;');
}