Some checks failed
Beta Release / beta (push) Failing after 48s
Adds a WXT-based browser extension that replaces manual JS snippet injection for AI-driven browser testing. The extension auto-connects to the Muyue server via WebSocket on every page, using the exact same protocol as the existing snippet — zero backend changes needed. - Chrome/Edge (MV3) + Firefox (MV2) from single codebase via WXT - Content script: auto-connect WS, console capture, URL tracking, RPC - Background service worker: token management, screenshots, badge - Popup + side panel with server status, sessions, URL config - CI workflows: build extension, attach .zip to releases - Makefile targets: ext, ext-chrome, ext-firefox, ext-zip - Version bumped to 0.8.0 Assisted-by: GLM-5.1 via Crush <crush@charm.land>
73 lines
2.2 KiB
JavaScript
73 lines
2.2 KiB
JavaScript
import '../../styles/panel.css';
|
|
import { getServerUrl, setServerUrl, fetchSessions } from '../../lib/config';
|
|
|
|
const $serverStatus = document.getElementById('server-status');
|
|
const $sessionCount = document.getElementById('session-count');
|
|
const $errorCount = document.getElementById('error-count');
|
|
const $sessionsList = document.getElementById('sessions-list');
|
|
const $btnDashboard = document.getElementById('btn-dashboard');
|
|
const $serverUrl = document.getElementById('server-url');
|
|
const $btnSaveUrl = document.getElementById('btn-save-url');
|
|
|
|
function dot(color) {
|
|
return `<span class="dot dot-${color}"></span>`;
|
|
}
|
|
|
|
function renderSessions(sessions) {
|
|
if (sessions.length === 0) {
|
|
$sessionsList.innerHTML = '';
|
|
return;
|
|
}
|
|
|
|
$sessionsList.innerHTML = `
|
|
<div class="status-card" style="margin-top:12px">
|
|
<div style="font-size:11px;color:var(--text-secondary);margin-bottom:8px;text-transform:uppercase;letter-spacing:0.5px">
|
|
Connected tabs
|
|
</div>
|
|
${sessions.map((s) => `
|
|
<div class="status-row">
|
|
<span style="overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:200px" title="${s.url}">
|
|
${s.title || s.url || s.id}
|
|
</span>
|
|
<span style="font-size:10px;color:var(--text-secondary);font-family:var(--font-mono)">
|
|
${s.id.slice(0, 8)}
|
|
</span>
|
|
</div>
|
|
`).join('')}
|
|
</div>
|
|
`;
|
|
}
|
|
|
|
async function refresh() {
|
|
const url = await getServerUrl();
|
|
$serverUrl.value = url;
|
|
$btnDashboard.href = url;
|
|
|
|
try {
|
|
const sessions = await fetchSessions();
|
|
$serverStatus.innerHTML = `${dot('green')} Online`;
|
|
$sessionCount.textContent = sessions.length;
|
|
renderSessions(sessions);
|
|
} catch {
|
|
$serverStatus.innerHTML = `${dot('red')} Offline`;
|
|
$sessionCount.textContent = '—';
|
|
$sessionsList.innerHTML = '';
|
|
}
|
|
|
|
chrome.runtime.sendMessage({ type: 'get_state' }, (state) => {
|
|
if (chrome.runtime.lastError || !state) return;
|
|
$errorCount.textContent = state.errorCount || 0;
|
|
});
|
|
}
|
|
|
|
$btnSaveUrl.addEventListener('click', async () => {
|
|
const url = $serverUrl.value.trim().replace(/\/$/, '');
|
|
if (url) {
|
|
await setServerUrl(url);
|
|
refresh();
|
|
}
|
|
});
|
|
|
|
refresh();
|
|
setInterval(refresh, 5000);
|