Files
Crosspoint/src/network/html/HomePage.html
T

428 lines
12 KiB
HTML
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.
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>CrossPoint Reader</title>
<style>
:root {
--font-color: #333;
--bg: #f5f5f5;
--title-color: #2c3e50;
--card-bg: #FFF;
--label-color: #7f8c8d;
--border-color: #eee;
--accent-color: rgb(110, 154, 130);
--accent-hover-color: #5a8c73;
}
@media (prefers-color-scheme: dark) {
:root {
--font-color: #f5f5f5;
--bg: #333;
--title-color: #ecf0f1;
--card-bg: #444;
--label-color: #bdc3c7;
--border-color: #555;
color-scheme: dark;
}
}
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
Oxygen, Ubuntu, sans-serif;
max-width: 800px;
margin: 0 auto;
padding: 20px;
background-color: var(--bg);
color: var(--font-color);
}
h1 {
color: var(--title-color);
border-bottom: 2px solid var(--accent-color);
padding-bottom: 10px;
}
h2 {
color: var(--title-color);
margin-top: 0;
}
.card {
background: var(--card-bg);
border-radius: 8px;
padding: 20px;
margin: 15px 0;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.section {
margin-top: 18px;
}
.section:first-of-type {
margin-top: 0;
}
.section-title {
font-size: 0.85em;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.05em;
color: var(--label-color);
margin: 0 0 4px 0;
padding-bottom: 4px;
border-bottom: 1px solid var(--accent-color);
}
.info-row {
display: flex;
justify-content: space-between;
align-items: center;
padding: 8px 0;
border-bottom: 1px solid var(--border-color);
}
.info-row:last-child {
border-bottom: none;
}
.label {
font-weight: 600;
color: var(--label-color);
}
.value {
color: var(--title-color);
}
.status {
display: inline-block;
padding: 4px 12px;
border-radius: 12px;
background-color: #27ae60;
color: white;
font-size: 0.9em;
}
.nav-links {
margin: 20px 0;
display: flex;
gap: 10px;
}
.nav-links a {
padding: 10px 20px;
color: var(--font-color);
text-decoration: none;
border-radius: 4px;
}
.nav-links a.active {
background-color: var(--accent-color);
color: white;
}
.nav-links a:not(.active):hover {
background-color: var(--accent-hover-color);
color: white;
}
.action-row {
text-align: center;
margin-top: 16px;
}
.action-row button {
padding: 10px 18px;
border: none;
border-radius: 8px;
background-color: var(--accent-color);
color: white;
font-size: 1rem;
cursor: pointer;
}
.action-row button:disabled {
opacity: 0.6;
cursor: default;
}
.debug-footer {
margin-top: 24px;
padding-top: 8px;
border-top: 1px dashed var(--border-color);
display: flex;
justify-content: space-between;
gap: 12px;
font-size: 0.75em;
color: var(--label-color);
opacity: 0.7;
}
</style>
</head>
<body>
<h1>📚 CrossPoint Reader</h1>
<div class="nav-links">
<a href="/files">File Manager</a>
<a href="/settings">Settings</a>
<a href="/systeminfo" class="active">System Info</a>
</div>
<div class="card">
<h2>Device Status</h2>
<div class="section">
<div class="section-title">Version</div>
<div class="info-row">
<span class="label">Firmware</span>
<span class="value" id="version">Loading...</span>
</div>
<div class="info-row">
<span class="label">Device</span>
<span class="value" id="device">Loading...</span>
</div>
</div>
<div class="section">
<div class="section-title">Chip</div>
<div class="info-row">
<span class="label">Chip</span>
<span class="value" id="chip-version">Loading...</span>
</div>
<div class="info-row">
<span class="label">CPU</span>
<span class="value" id="cpu-mhz">Loading...</span>
</div>
</div>
<div class="section">
<div class="section-title">Network</div>
<div class="info-row">
<span class="label">WiFi Status</span>
<span class="status" id="wifi-status">Loading...</span>
</div>
<div class="info-row">
<span class="label">IP Address</span>
<span class="value" id="ip-address">Loading...</span>
</div>
<div class="info-row">
<span class="label">MAC Address</span>
<span class="value" id="mac-address">Loading...</span>
</div>
</div>
<div class="section">
<div class="section-title">Memory</div>
<div class="info-row">
<span class="label">Free / Min / Max</span>
<span class="value" id="heap-combined">Loading...</span>
</div>
</div>
<div class="section">
<div class="section-title">Flash</div>
<div class="info-row">
<span class="label">App partition</span>
<span class="value" id="app-partition-size">Loading...</span>
</div>
<div class="info-row">
<span class="label">Total flash</span>
<span class="value" id="flash-total">Loading...</span>
</div>
</div>
<div class="section">
<div class="section-title">Runtime</div>
<div class="info-row">
<span class="label">Uptime</span>
<span class="value" id="uptime">Loading...</span>
</div>
<div class="info-row">
<span class="label">Battery</span>
<span class="value" id="battery">Loading...</span>
</div>
</div>
<div class="section">
<div class="section-title">Storage</div>
<div class="info-row">
<span class="label">SD Card</span>
<span class="value" id="sd-space">Not loaded</span>
</div>
<div class="action-row">
<button id="load-sd-button">Load SD info</button>
</div>
</div>
<div class="debug-footer">
<span>Fast fetch: <span id="fast-status-duration"></span></span>
<span>Full fetch: <span id="full-status-duration"></span></span>
</div>
</div>
<div class="card">
<p style="text-align: center; color: #95a5a6; margin: 0">
CrossPoint E-Reader • Open Source
</p>
</div>
<script>
function pickUnit(maxBytes) {
const units = ['B', 'KB', 'MB', 'GB'];
let div = 1, i = 0;
while (maxBytes >= 1024 && i < units.length - 1) { maxBytes /= 1024; div *= 1024; i++; }
return { unit: units[i], div };
}
function formatBytes(bytes) {
if (bytes == null) return 'N/A';
const { unit, div } = pickUnit(bytes);
if (div === 1) return bytes.toLocaleString() + ' ' + unit;
return parseFloat((bytes / div).toFixed(2)).toLocaleString() + ' ' + unit;
}
function formatBytesTriple(a, b, c) {
if (a == null || b == null || c == null) return 'N/A';
const { unit, div } = pickUnit(Math.max(a, b, c));
const fmt = (v) => div === 1 ? v.toLocaleString() : parseFloat((v / div).toFixed(2)).toLocaleString();
return fmt(a) + ' / ' + fmt(b) + ' / ' + fmt(c) + ' ' + unit;
}
const SD_LABELS = {
NOT_LOADED: 'Not loaded',
LOAD_BUTTON: 'Load SD info',
LOADING: 'Loading...',
ERROR: 'Error loading'
};
let sdLoading = false;
function formatUptime(seconds) {
if (seconds == null) return 'N/A';
const h = Math.floor(seconds / 3600);
const m = Math.floor((seconds % 3600) / 60);
const s = seconds % 60;
return h + 'h ' + String(m).padStart(2, '0') + 'm ' + String(s).padStart(2, '0') + 's';
}
function formatDuration(ms) {
return ms == null ? 'N/A' : ms + ' ms';
}
function applyStatus(data, includeSd) {
document.getElementById('version').textContent = data.version || 'N/A';
const deviceLabel = data.deviceType
? (data.displayWidth && data.displayHeight
? data.deviceType + ' (' + data.displayWidth + ' × ' + data.displayHeight + ' px)'
: data.deviceType)
: 'N/A';
document.getElementById('device').textContent = deviceLabel;
document.getElementById('chip-version').textContent = data.chipVersion || 'N/A';
document.getElementById('cpu-mhz').textContent = data.cpuMHz
? data.cpuMHz.toLocaleString() + ' MHz'
: 'N/A';
const wifiStatus = document.getElementById('wifi-status');
wifiStatus.textContent = data.mode || 'N/A';
document.getElementById('ip-address').textContent = data.ip || 'N/A';
document.getElementById('mac-address').textContent = data.macAddress || 'N/A';
document.getElementById('heap-combined').textContent =
formatBytesTriple(data.freeHeap, data.minFreeHeap, data.maxAllocHeap);
document.getElementById('app-partition-size').textContent = data.appPartitionSize != null
? formatBytes(data.appPartitionSize)
: 'N/A';
document.getElementById('flash-total').textContent = data.flashTotal != null
? formatBytes(data.flashTotal)
: 'N/A';
document.getElementById('battery').textContent = (data.batteryPercent != null)
? (data.batteryPercent + '%' + (data.charging ? ' (Charging)' : ''))
: 'N/A';
document.getElementById('uptime').textContent = formatUptime(data.uptime);
if (includeSd) {
document.getElementById('sd-space').textContent = data.sdTotal
? formatBytes(data.sdUsed) + ' / ' + formatBytes(data.sdTotal)
: 'N/A';
} else if (!sdLoading) {
document.getElementById('sd-space').textContent = SD_LABELS.NOT_LOADED;
}
}
async function fetchStatus() {
const start = performance.now();
try {
const fastResponse = await fetch('/api/status/fast');
if (!fastResponse.ok) {
throw new Error('Failed to fetch fast status: ' + fastResponse.status + ' ' + fastResponse.statusText);
}
const fastData = await fastResponse.json();
applyStatus(fastData, false);
document.getElementById('fast-status-duration').textContent = formatDuration(Math.round(performance.now() - start));
} catch (error) {
console.error('Error fetching status:', error);
document.getElementById('sd-space').textContent = SD_LABELS.ERROR;
document.getElementById('fast-status-duration').textContent = 'Error';
} finally {
if (!sdLoading) {
setSdButtonState(true);
}
}
}
async function fetchSdStatus() {
if (sdLoading) {
return;
}
const button = document.getElementById('load-sd-button');
sdLoading = true;
button.disabled = true;
button.textContent = SD_LABELS.LOADING;
document.getElementById('sd-space').textContent = SD_LABELS.LOADING;
document.getElementById('full-status-duration').textContent = 'Loading...';
const start = performance.now();
try {
const response = await fetch('/api/status');
if (!response.ok) {
throw new Error('Failed to fetch SD status: ' + response.status + ' ' + response.statusText);
}
const data = await response.json();
applyStatus(data, true);
button.style.display = 'none';
document.getElementById('full-status-duration').textContent = formatDuration(Math.round(performance.now() - start));
} catch (error) {
console.error('Error fetching SD status:', error);
button.disabled = false;
button.textContent = SD_LABELS.LOAD_BUTTON;
document.getElementById('sd-space').textContent = SD_LABELS.ERROR;
document.getElementById('full-status-duration').textContent = 'Error';
} finally {
sdLoading = false;
}
}
function setSdButtonState(enabled) {
const button = document.getElementById('load-sd-button');
button.disabled = !enabled;
button.textContent = enabled ? SD_LABELS.LOAD_BUTTON : SD_LABELS.LOADING;
}
document.addEventListener('DOMContentLoaded', () => {
setSdButtonState(false);
document.getElementById('load-sd-button').addEventListener('click', () => {
if (sdLoading) {
return;
}
fetchSdStatus();
});
fetchStatus();
});
</script>
</body>
</html>