Files
VoxVera/gui/electron/index.html
T

153 lines
5.6 KiB
HTML

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>VoxVera GUI</title>
<style>
body {
font-family: sans-serif;
margin: 1rem;
}
#status {
border: 1px solid #ccc;
padding: 0.75rem;
margin-bottom: 1rem;
background: #fafafa;
}
.status-badge {
display: inline-block;
padding: 0.15rem 0.4rem;
border-radius: 999px;
font-size: 0.85rem;
font-weight: bold;
text-transform: uppercase;
}
.status-supported {
background: #dff3e4;
color: #14532d;
}
.status-experimental {
background: #fff4d6;
color: #854d0e;
}
.status-error {
background: #fee2e2;
color: #991b1b;
}
#log {
border: 1px solid #ccc;
padding: 5px;
height: 150px;
overflow-y: auto;
background: #f8f8f8;
white-space: pre-wrap;
}
</style>
</head>
<body>
<h1>VoxVera</h1>
<div id="status">
<strong>Runtime status</strong>
<div id="platform-tier"></div>
<div id="platform-model"></div>
<div id="autostart-state"></div>
<div id="doctor-summary"></div>
</div>
<form id="config-form">
<div><label>Name <input type="text" id="name"></label></div>
<div><label>Subdomain <input type="text" id="folder_name"></label></div>
<div><label>Title <input type="text" id="title"></label></div>
<div><label>Subtitle <input type="text" id="subtitle"></label></div>
<div><label>Headline <input type="text" id="headline"></label></div>
<div><label>Content<br><textarea id="content" rows="5" cols="60"></textarea></label></div>
<div><label>URL Message <input type="text" id="url_message"></label></div>
<div><label>URL <input type="text" id="url"></label></div>
<div><label>Tear-off link <input type="text" id="tear_off_link"></label></div>
<div><label>Footer message <input type="text" id="footer_message"></label></div>
<button type="button" id="quickstart">Generate &amp; Serve</button>
</form>
<p id="onion-address"></p>
<pre id="log"></pre>
<script>
function setStatusBadge(targetId, label, tier) {
const el = document.getElementById(targetId);
const className = tier === 'supported' ? 'status-supported'
: tier === 'experimental' ? 'status-experimental'
: 'status-error';
el.innerHTML = `<span class="status-badge ${className}">${label}</span>`;
}
function summarizeDoctorChecks(doctor) {
const sections = doctor && doctor.sections ? Object.values(doctor.sections) : [];
if (sections.length > 0) {
const failedSections = sections.filter(section => section.checks && section.checks.length > 0 && !section.ok);
if (failedSections.length === 0) return 'All doctor sections passed.';
return `Doctor warnings in: ${failedSections.map(section => section.title).join(', ')}`;
}
const checks = doctor && Array.isArray(doctor.checks) ? doctor.checks : [];
if (checks.length === 0) return 'No diagnostics available.';
const failed = checks.filter(check => !check.ok);
if (failed.length === 0) return 'All doctor checks passed.';
return `Doctor reports ${failed.length} failing check(s): ${failed.map(check => check.name).join(', ')}`;
}
async function loadRuntimeStatus() {
const runtime = await window.voxvera.loadRuntimeStatus();
if (!runtime.ok) {
setStatusBadge('platform-tier', 'error', 'error');
document.getElementById('platform-model').textContent = runtime.error;
document.getElementById('autostart-state').textContent = '';
document.getElementById('doctor-summary').textContent = '';
return;
}
const { platformStatus, autostart, doctor } = runtime;
setStatusBadge('platform-tier', platformStatus.tier, platformStatus.tier);
document.getElementById('platform-model').textContent = platformStatus.hosting_model || '';
if (autostart && !autostart.error) {
document.getElementById('autostart-state').textContent = `Autostart: ${autostart.message}`;
} else if (autostart && autostart.error) {
document.getElementById('autostart-state').textContent = `Autostart status unavailable: ${autostart.error}`;
}
document.getElementById('doctor-summary').textContent = summarizeDoctorChecks(doctor);
}
async function load() {
const cfg = await window.voxvera.loadConfig();
for (const [k, v] of Object.entries(cfg)) {
const el = document.getElementById(k);
if (el) el.value = v;
}
await loadRuntimeStatus();
}
document.addEventListener('DOMContentLoaded', load);
document.getElementById('quickstart').addEventListener('click', async () => {
const ids = ['name','folder_name','title','subtitle','headline','content','url_message','url','tear_off_link','footer_message'];
const cfg = {};
ids.forEach(id => {
const el = document.getElementById(id);
if (el) cfg[id] = el.value;
});
await window.voxvera.quickstart(cfg);
await loadRuntimeStatus();
});
window.voxvera.onOnionUrl(url => {
document.getElementById('onion-address').textContent = `Onion address: ${url}`;
const tear = document.getElementById('tear_off_link');
if (tear) tear.value = url;
});
window.voxvera.onLog((msg, isErr) => {
const container = document.getElementById('log');
const span = document.createElement('span');
if (isErr) span.style.color = 'red';
span.textContent = msg;
container.appendChild(span);
container.scrollTop = container.scrollHeight;
});
</script>
</body>
</html>