Add dashboard UI updates and settings modal

This commit is contained in:
Aaron
2025-12-10 18:51:31 -05:00
commit c85df728b7
54 changed files with 7151 additions and 0 deletions

634
pikit-web/index.html Normal file
View File

@@ -0,0 +1,634 @@
<!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>
</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="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="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="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 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"></script>
<div id="toastContainer" class="toast-container"></div>
</body>
</html>