Files
pi-kit/pikit-web/index.html

784 lines
30 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 lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Pi-Kit Dashboard</title>
<link rel="stylesheet" href="assets/style.css" />
</head>
<body>
<header class="topbar">
<div class="brand">
<div
class="dot"
title="Dashboard status indicator (shows when the UI is loaded)"
aria-label="Dashboard status indicator"
></div>
<span>Pi-Kit</span>
</div>
<div class="top-indicators">
<span class="chip-label">Status</span>
<span id="updatesFlagTop" class="status-chip quiet">Auto updates</span>
<span id="updatesNoteTop" class="hint quiet"></span>
<span id="refreshFlagTop" class="status-chip quiet">Refresh: 10s</span>
<span id="tempFlagTop" class="status-chip quiet">Temp: OK</span>
<span id="releaseFlagTop" class="status-chip quiet">Pi-Kit: n/a</span>
</div>
<div class="top-actions">
<button
id="themeToggle"
class="ghost icon-btn"
title="Toggle theme"
aria-label="Toggle theme"
>
<span id="themeToggleIcon" aria-hidden="true">&#127769;</span>
</button>
<button id="diagLogBtn" class="ghost hidden" title="Diagnostics log (visible when enabled)">
Log
</button>
<button id="releaseBtn" class="ghost" title="Pi-Kit updates">
Update
</button>
<button id="aboutBtn" class="ghost" title="About Pi-Kit">About</button>
<button id="helpBtn" class="ghost" title="Open help">Help</button>
<button id="advBtn" class="ghost" title="Open settings">
Settings
</button>
</div>
</header>
<main class="layout">
<section class="hero">
<div>
<p class="eyebrow">All-in-one launcher</p>
<h1>Welcome to your Pi-Kit homebase</h1>
<p class="lede">
Launch services, view quick health, and handle essentials without
cracking open SSH.
</p>
</div>
<div class="hero-stats" id="heroStats"></div>
</section>
<section class="panel">
<div class="panel-header">
<div>
<p class="eyebrow">Configured services</p>
<h2>Web interfaces</h2>
<p class="hint">
Shortcuts to the web UIs running on your Pi. Click a card to open it. Use the + button to add another service; use the ⋮ menu on a card to edit or remove it.
</p>
</div>
<div class="panel-actions">
<button id="addServiceOpen" class="ghost icon-btn" title="Add a service" aria-label="Add service">
+
</button>
</div>
</div>
<div id="servicesGrid" class="grid"></div>
</section>
</main>
<div id="readyOverlay" class="overlay hidden">
<div class="overlay-box">
<h3>Finishing setup</h3>
<p>
This only takes a couple of minutes. You'll see the dashboard once
Pi-Kit setup completes.
</p>
<div class="spinner"></div>
</div>
</div>
<div id="changelogModal" class="modal hidden">
<div class="modal-card wide">
<div class="panel-header sticky">
<div>
<p class="eyebrow">Changelog</p>
<h3 id="changelogTitle">Release notes</h3>
</div>
<button id="changelogClose" class="ghost icon-btn close-btn" title="Close changelog">
&times;
</button>
</div>
<pre id="changelogBody" class="log-box" aria-live="polite"></pre>
</div>
</div>
<div id="diagModal" class="modal hidden diag-log-modal">
<div class="modal-card wide">
<div class="panel-header sticky">
<div>
<p class="eyebrow">Diagnostics</p>
<h3>Diagnostics log</h3>
<p class="hint">RAM-only; cleared on reboot/clear. Use toggles in Settings → Diagnostics to enable.</p>
</div>
<button id="diagClose" class="ghost icon-btn close-btn" title="Close diagnostics log">
&times;
</button>
</div>
<div class="control-actions wrap gap">
<button id="diagRefreshBtn" class="ghost">Refresh</button>
<button id="diagClearBtn" class="ghost">Clear</button>
<button id="diagCopyBtn" class="ghost">Copy</button>
<button id="diagDownloadBtn" class="ghost">Download</button>
<span id="diagStatusModal" class="hint quiet"></span>
</div>
<pre id="diagLogBox" class="log-box" aria-live="polite"></pre>
</div>
</div>
<div id="releaseModal" class="modal hidden">
<div class="modal-card wide">
<div class="panel-header sticky">
<div>
<p class="eyebrow">Updates</p>
<h3>Pi-Kit releases</h3>
<p class="hint">
Check for a new Pi-Kit release, download it, and install from here.
</p>
</div>
<button id="releaseClose" class="ghost icon-btn close-btn" title="Close updates panel">
&times;
</button>
</div>
<div class="controls column">
<div class="control-card release-versions">
<div>
<p class="hint quiet">Current version</p>
<h3 id="releaseCurrent">n/a</h3>
</div>
<div class="align-right">
<p class="hint quiet">Latest available</p>
<h3 id="releaseLatest"></h3>
<p id="releaseStatusMsg" class="hint status-msg"></p>
<button id="releaseChangelogBtn" class="ghost small" title="View changelog">Changelog</button>
</div>
</div>
<div class="control-actions split-row">
<button id="releaseCheckBtn" class="ghost" title="Check for a new release">
Check
</button>
<button id="releaseApplyBtn" title="Download and install the latest release">
Upgrade
</button>
<button id="releaseRollbackBtn" class="ghost" title="Rollback to previous backup">
Rollback
</button>
<label class="checkbox-row inline">
<input type="checkbox" id="releaseAutoCheck" />
<span>Auto-check daily</span>
</label>
<label class="checkbox-row inline">
<input type="checkbox" id="releaseChannelToggle" />
<span>Allow dev builds</span>
</label>
</div>
<div id="releaseProgress" class="hint status-msg"></div>
<div class="log-card">
<div class="log-header">
<span class="hint quiet">Update console</span>
<div class="log-actions">
<button id="releaseLogCopy" class="ghost icon-btn" title="Copy log" aria-label="Copy log">
</button>
<span id="releaseLogStatus" class="hint quiet"></span>
</div>
</div>
<pre id="releaseLog" class="log-box" aria-live="polite"></pre>
</div>
</div>
</div>
</div>
<div id="busyOverlay" class="overlay hidden">
<div class="overlay-box">
<h3 id="busyTitle">Working…</h3>
<p id="busyText" class="hint">This may take a few seconds.</p>
<div class="spinner"></div>
</div>
</div>
<div id="aboutModal" class="modal hidden">
<div class="modal-card wide">
<div class="panel-header sticky">
<div>
<p class="eyebrow">About</p>
<h3>About Pi-Kit</h3>
</div>
<button id="aboutClose" class="ghost icon-btn close-btn" title="Close about">
&times;
</button>
</div>
<div class="help-body">
<h4>What is this?</h4>
<p>
Pi-Kit is a self-hosted dashboard for managing services, monitoring your Pi,
and handling unattended updates, all through a single UI.
</p>
<h4>Font licensing</h4>
<p>
The following fonts are included under the SIL Open Font License 1.1 (no attribution required):
</p>
<ul>
<li>Red Hat Display / Red Hat Text</li>
<li>Space Grotesk</li>
<li>Manrope</li>
<li>DM Sans</li>
<li>Sora</li>
<li>Chivo</li>
<li>Atkinson Hyperlegible</li>
<li>IBM Plex Sans</li>
</ul>
<p class="hint">OFL license text is bundled at assets/fonts/OFL.txt.</p>
<h4>Other licenses</h4>
<p>
Pi-Kit code is MIT licensed. Third-party tooling (Vite, @fontsource) is MIT; Playwright (dev/test) is Apache 2.0.
See <a href="/THIRD-PARTY-LICENSES.txt" target="_blank">THIRD-PARTY-LICENSES.txt</a> and <a href="/LICENSE.txt" target="_blank">LICENSE.txt</a> for details, and <a href="/assets/fonts/OFL.txt" target="_blank">OFL.txt</a> for font licensing.
</p>
</div>
</div>
</div>
<div id="confirmModal" class="modal hidden">
<div class="modal-card">
<div class="panel-header">
<div>
<p class="eyebrow">Confirm action</p>
<h3 id="confirmTitle">Are you sure?</h3>
<p id="confirmBody" class="hint"></p>
</div>
</div>
<div class="control-actions right">
<button id="confirmCancel" class="ghost">Cancel</button>
<button id="confirmOk">Continue</button>
</div>
</div>
</div>
<div id="addServiceModal" class="modal hidden">
<div class="modal-card wide">
<div class="panel-header sticky">
<div>
<p class="eyebrow">Add service</p>
<h3>Register a web interface</h3>
<p class="hint">
Adds a local service by name and port. Choose HTTP or HTTPS to match how it serves traffic.
</p>
</div>
<button id="addSvcClose" class="ghost icon-btn close-btn" title="Close add service">
&times;
</button>
</div>
<div class="controls column">
<div class="control-actions column">
<input
type="text"
id="svcName"
placeholder="Service name"
maxlength="32"
/>
<p class="hint quiet">Service name: max 32 characters.</p>
<input
type="number"
id="svcPort"
placeholder="Port (e.g. 8080)"
min="1"
max="65535"
/>
<input
type="text"
id="svcPath"
placeholder="Optional path (e.g. /admin)"
/>
<div class="control-row split">
<label class="checkbox-row">
<span>Protocol</span>
<select id="svcScheme">
<option value="http">HTTP</option>
<option value="https">HTTPS</option>
</select>
</label>
<label class="checkbox-row inline tight nowrap">
<input type="checkbox" id="svcSelfSigned" />
<span>Self-signed TLS</span>
</label>
</div>
<textarea
id="svcNotice"
rows="3"
placeholder="Optional notice (shown on card)"
></textarea>
<input
type="text"
id="svcNoticeLink"
placeholder="Optional link for more info"
/>
<div class="control-actions">
<button id="svcAddBtn" title="Add service and open port on LAN">
Add
</button>
</div>
<div id="svcMsg" class="hint status-msg"></div>
</div>
</div>
</div>
</div>
<div id="advModal" class="modal hidden">
<div class="modal-card wide">
<div class="panel-header sticky">
<div>
<p class="eyebrow warning">Settings</p>
<h3>System controls</h3>
</div>
<button
id="advClose"
class="ghost icon-btn close-btn"
title="Close settings panel"
>
&times;
</button>
</div>
<div class="controls accordion-list">
<div class="accordion">
<button class="accordion-toggle" data-target="acc-updates">
Automatic updates
</button>
<div class="accordion-body" id="acc-updates">
<p class="hint">
Daily unattended-upgrades: choose scope, schedule, cleanup, bandwidth, and reboot policy.
</p>
<div class="control-actions column" id="updatesSection">
<div class="control-actions">
<span id="updatesStatus" class="status-chip">Unknown</span>
<label class="toggle" title="Toggle unattended updates">
<input type="checkbox" id="updatesToggle" />
<span class="slider"></span>
</label>
<span class="hint" id="updatesToggleLabel">Enable/disable unattended upgrades</span>
</div>
<div id="updatesControls">
<div class="form-grid">
<label class="field">
<span>What to update</span>
<select id="updatesScope">
<option value="all">Security + regular updates</option>
<option value="security">Security only</option>
</select>
</label>
<label class="field">
<span>Download updates at</span>
<input type="time" id="updateTimeInput" value="04:00" title="Time of day to download updates" />
</label>
<label class="field">
<span>Install updates at</span>
<input type="time" id="upgradeTimeInput" value="04:30" title="Time of day to install updates" />
</label>
<div class="field checkbox-field">
<span>Cleanup unused packages</span>
<label class="checkbox-row inline tight">
<input type="checkbox" id="updatesCleanup" title="Automatically remove unused dependencies after upgrades" />
<span>Auto-remove dependencies after upgrades</span>
</label>
</div>
<label class="field">
<span>Bandwidth limit (KB/s, 0 = unlimited)</span>
<input
type="number"
id="updatesBandwidth"
min="0"
placeholder="0"
/>
</label>
<div class="field checkbox-field">
<span>Reboot options</span>
<div class="control-actions column tight">
<label class="checkbox-row inline tight">
<input type="checkbox" id="updatesRebootToggle" title="Auto-reboot when updates require it" />
<span>Auto-reboot if required</span>
</label>
<label class="checkbox-row inline tight">
<span>Reboot time</span>
<input type="time" id="updatesRebootTime" value="04:30" title="Scheduled reboot time when auto-reboot is enabled" />
</label>
<label class="checkbox-row inline tight nowrap">
<input type="checkbox" id="updatesRebootUsers" title="Allow reboot even if users are logged in" />
<span>Allow reboot with active users</span>
</label>
</div>
</div>
</div>
<div class="control-actions split-row">
<button id="updatesSaveBtn" title="Save unattended-upgrades settings" disabled>
Save settings
</button>
<span id="updatesUnsavedNote" class="note-warn hidden">Please save changes or they will not apply.</span>
</div>
<div id="updatesMsg" class="hint status-msg"></div>
</div>
</div>
</div>
</div>
<div class="accordion">
<button class="accordion-toggle" data-target="acc-anim">
Animations
</button>
<div class="accordion-body" id="acc-anim">
<p class="hint">
Adds smooth hover and click motion cues across the dashboard.
Default is on.
</p>
<div class="control-actions">
<span class="status-chip">Enable animations</span>
<label class="toggle" title="Toggle UI animations">
<input type="checkbox" id="animToggle" checked />
<span class="slider"></span>
</label>
</div>
<div class="form-grid">
<label class="field">
<span>Toast position</span>
<select id="toastPosSelect" title="Choose where toast notifications appear">
<option value="bottom-center">Bottom center</option>
<option value="bottom-right">Bottom right</option>
<option value="bottom-left">Bottom left</option>
<option value="top-right">Top right</option>
<option value="top-left">Top left</option>
<option value="top-center">Top center</option>
</select>
</label>
<label class="field">
<span>Toast animation</span>
<select id="toastAnimSelect" title="Toast entry animation style">
<option value="slide-in">Slide in</option>
<option value="fade">Fade</option>
<option value="pop">Pop</option>
<option value="bounce">Bounce</option>
<option value="drop">Drop</option>
<option value="grow">Grow</option>
</select>
</label>
<label class="field">
<span>Toast speed (ms)</span>
<input
type="number"
id="toastSpeedInput"
min="100"
max="3000"
step="50"
value="280"
title="Animation duration for toasts"
/>
</label>
<label class="field">
<span>Toast duration (ms)</span>
<input
type="number"
id="toastDurationInput"
min="1000"
max="15000"
step="250"
value="5000"
title="How long to keep toasts visible"
/>
</label>
</div>
<div class="control-actions">
<button id="toastTestBtn" title="Show a sample toast">Test toast</button>
</div>
</div>
</div>
<div class="accordion">
<button class="accordion-toggle" data-target="acc-appearance">
Appearance
</button>
<div class="accordion-body" id="acc-appearance">
<p class="hint">Choose the dashboard typeface.</p>
<div class="form-grid">
<label class="field">
<span>Dashboard font</span>
<select id="fontSelect" title="Choose dashboard typeface">
<option value="redhat">Red Hat (default)</option>
<option value="space">Space Grotesk</option>
<option value="manrope">Manrope</option>
<option value="dmsans">DM Sans</option>
<option value="sora">Sora</option>
<option value="chivo">Chivo</option>
<option value="atkinson">Atkinson Hyperlegible</option>
<option value="plex">IBM Plex Sans</option>
</select>
</label>
</div>
</div>
</div>
<div class="accordion">
<button class="accordion-toggle" data-target="acc-refresh">
Refresh interval
</button>
<div class="accordion-body" id="acc-refresh">
<p class="hint">
Sets how often status and services auto-refresh. Minimum 5
seconds; default 10.
</p>
<div class="control-actions column">
<label class="checkbox-row">
<span>Seconds</span>
<input
type="number"
id="refreshIntervalInput"
min="5"
max="120"
value="10"
/>
</label>
<div class="control-actions">
<button
id="refreshIntervalSave"
title="Update auto refresh rate"
>
Save
</button>
</div>
<div id="refreshIntervalMsg" class="hint status-msg"></div>
</div>
</div>
</div>
<div class="accordion">
<button class="accordion-toggle" data-target="acc-diag">
Diagnostics
</button>
<div class="accordion-body" id="acc-diag">
<p class="hint">
Temporary, RAM-only logs for debugging. Toggle on, choose debug for extra detail. Logs reset on reboot or clear. Use the Log button in the top bar (visible when diagnostics is enabled) to view, copy, download, or clear entries.
</p>
<div class="control-actions split-row">
<label class="checkbox-row inline tight">
<input type="checkbox" id="diagEnableToggle" />
<span>Enable diagnostics</span>
</label>
<label class="checkbox-row inline tight">
<input type="checkbox" id="diagDebugToggle" />
<span>Debug detail (includes UI clicks)</span>
</label>
</div>
<div class="control-actions">
<span id="diagStatus" class="hint quiet"></span>
</div>
<p class="hint quiet">Stored in RAM (max ~1MB server, ~500 entries client). No data written to disk.</p>
</div>
</div>
<div class="accordion">
<button class="accordion-toggle danger-btn" data-target="acc-reset">
Factory reset
</button>
<div class="accordion-body" id="acc-reset">
<p class="hint">
Restores Pi-Kit defaults, resets firewall and passwords
(root/dietpi &rarr; pikit), then reboots. Type YES to confirm.
</p>
<div class="control-actions column">
<input
type="text"
id="resetConfirm"
placeholder="Type YES to confirm"
/>
<button
id="resetBtn"
class="danger-btn"
disabled
title="Type YES above to enable"
>
Factory reset
</button>
</div>
</div>
</div>
</div>
</div>
</div>
<div id="helpModal" class="modal hidden">
<div class="modal-card wide">
<div class="panel-header">
<div>
<p class="eyebrow">Help</p>
<h3>How Pi-Kit dashboard works</h3>
<p class="hint">Quick, friendly guidance for common tasks.</p>
</div>
<button
id="helpClose"
class="ghost icon-btn close-btn"
title="Close help"
>
&times;
</button>
</div>
<div class="help-body">
<h4>Quick tour</h4>
<ul>
<li><strong>Status chips</strong> show uptime, OS, CPU temp, memory/disk, LAN IP, and auto-update/reboot flags.</li>
<li><strong>Service cards</strong> open your web UIs; the “…” menu lets you rename, change port/protocol/path, or remove.</li>
<li><strong>Hero stats</strong> up top give you a fast health snapshot.</li>
</ul>
<h4>Adding and editing services</h4>
<ul>
<li>Use <strong>Web interfaces → Add service</strong> to register a local UI. Pick HTTP/HTTPS to match the service.</li>
<li>Mark “Selfsigned TLS” if the cert isnt trusted to avoid surprise warnings.</li>
<li>Paths should start with “/” (e.g., <code>/admin</code>); ports must be 165535.</li>
</ul>
<h4>Automatic updates</h4>
<ul>
<li>Turn unattended upgrades on/off, choose securityonly or all updates.</li>
<li>Set download/install times, bandwidth limit, cleanup, and reboot policy.</li>
<li>Save to apply; the top chip shows if a reboot is required/scheduled.</li>
</ul>
<h4>Appearance & toasts</h4>
<ul>
<li>Pick a font in <strong>Settings → Appearance</strong> (Red Hat, Space Grotesk, Manrope, etc.).</li>
<li>Customize toast position, animation, speed, and duration under <strong>Animations</strong>.</li>
</ul>
<h4>Passwords & SSH (friendly defaults)</h4>
<ul>
<li>Default password for <strong>root</strong> and <strong>dietpi</strong>: <code>pikit</code>. Please change it ASAP.</li>
<li>To use keys: <code>ssh-copy-id -i ~/.ssh/yourkey.pub user@pikit</code>, then test with <code>ssh -i ~/.ssh/yourkey user@pikit</code>.</li>
<li>Once keys work, consider disabling password auth in <code>/etc/ssh/sshd_config</code> for extra safety.</li>
</ul>
<h4>Safety</h4>
<ul>
<li>Firewall allows LAN by default; nothing is exposed to the internet by PiKit.</li>
<li>Factory reset reverts passwords and firewall rules and reboots. Use only when you need a clean slate.</li>
</ul>
<h4>Troubleshooting</h4>
<ul>
<li>Service link fails? Make sure the service runs, port/path are right, and edit via the “…” menu.</li>
<li>Key login fails? Re-run <code>ssh-copy-id</code> for the correct user and retry.</li>
</ul>
</div>
</div>
</div>
<div id="menuModal" class="modal hidden">
<div class="modal-card wide">
<div class="panel-header">
<div>
<p class="eyebrow">Service actions</p>
<h3 id="menuTitle"></h3>
<p id="menuSubtitle" class="hint"></p>
</div>
<button class="ghost icon-btn close-btn" id="menuClose" title="Close">
&times;
</button>
</div>
<div class="config-list">
<div class="config-row">
<div class="config-label">
<h4>Rename</h4>
<p class="hint">Update the display name (max 32 characters).</p>
</div>
<div class="config-controls">
<input type="text" id="menuRename" maxlength="32" />
</div>
</div>
<div class="config-row">
<div class="config-label">
<h4>Change port</h4>
<p class="hint">
Moves the service to a new port and opens it on LAN.
</p>
</div>
<div class="config-controls">
<input type="number" id="menuPort" min="1" max="65535" />
</div>
</div>
<div class="config-row">
<div class="config-label">
<h4>Path</h4>
<p class="hint">Optional subpath (e.g. /admin or /ui/).</p>
</div>
<div class="config-controls">
<input type="text" id="menuPath" placeholder="/admin" />
</div>
</div>
<div class="config-row">
<div class="config-label">
<h4>Protocol</h4>
<p class="hint">Choose HTTP or HTTPS link.</p>
</div>
<div class="config-controls">
<select id="menuScheme">
<option value="http">HTTP</option>
<option value="https">HTTPS</option>
</select>
</div>
</div>
<div class="config-row">
<div class="config-label">
<h4>Self-signed TLS</h4>
<p class="hint">Show a self-signed badge.</p>
</div>
<div class="config-controls">
<label class="checkbox-row">
<input type="checkbox" id="menuSelfSigned" />
<span>Mark as self-signed</span>
</label>
</div>
</div>
<div class="config-row">
<div class="config-label">
<h4>Notice</h4>
<p class="hint">Optional badge + info text on the card.</p>
</div>
<div class="config-controls">
<textarea id="menuNotice" rows="4" placeholder="e.g., Uses a self-signed certificate."></textarea>
<input
type="text"
id="menuNoticeLink"
placeholder="Optional link for more info"
/>
</div>
</div>
<div id="menuMsg" class="hint status-msg"></div>
</div>
<div class="modal-actions">
<button
id="menuRemoveBtn"
class="danger-btn"
title="Remove this service"
>
Remove
</button>
<div class="push"></div>
<button id="menuCancelBtn" class="ghost" title="Cancel changes">
Cancel
</button>
<button id="menuSaveBtn" class="primary" title="Save changes">
Save
</button>
</div>
</div>
</div>
<script type="module" src="assets/main.js?v=20251213j"></script>
<div id="toastContainer" class="toast-container"></div>
</body>
</html>