mirror of
https://github.com/l5yth/potato-mesh.git
synced 2026-07-06 01:42:11 +02:00
64f8862676
* Refactor front-end assets into external modules * Restore chat flag inline script * Declare legend toggle control variable * Remove dynamic background generation * Restore background script with theme-based color * run rufo
185 lines
10 KiB
Plaintext
185 lines
10 KiB
Plaintext
<!doctype html>
|
||
|
||
<!--
|
||
Copyright (C) 2025 l5yth
|
||
|
||
Licensed under the Apache License, Version 2.0 (the "License");
|
||
you may not use this file except in compliance with the License.
|
||
You may obtain a copy of the License at
|
||
|
||
http://www.apache.org/licenses/LICENSE-2.0
|
||
|
||
Unless required by applicable law or agreed to in writing, software
|
||
distributed under the License is distributed on an "AS IS" BASIS,
|
||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
See the License for the specific language governing permissions and
|
||
limitations under the License.
|
||
-->
|
||
|
||
<html lang="en">
|
||
<head>
|
||
<meta name="color-scheme" content="dark light">
|
||
<meta charset="utf-8" />
|
||
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
||
<% meta_title_html = Rack::Utils.escape_html(meta_title) %>
|
||
<% meta_name_html = Rack::Utils.escape_html(meta_name) %>
|
||
<% meta_description_html = Rack::Utils.escape_html(meta_description) %>
|
||
<% request_path = request.path.to_s.empty? ? "/" : request.path %>
|
||
<% canonical_url = "#{request.base_url}#{request_path}" %>
|
||
<% canonical_html = Rack::Utils.escape_html(canonical_url) %>
|
||
<% logo_url = "#{request.base_url}/potatomesh-logo.svg" %>
|
||
<% logo_url_html = Rack::Utils.escape_html(logo_url) %>
|
||
<% logo_alt_html = Rack::Utils.escape_html("#{meta_name} logo") %>
|
||
<title><%= meta_title_html %></title>
|
||
<meta name="application-name" content="<%= meta_name_html %>" />
|
||
<meta name="apple-mobile-web-app-title" content="<%= meta_name_html %>" />
|
||
<meta name="description" content="<%= meta_description_html %>" />
|
||
<link rel="canonical" href="<%= canonical_html %>" />
|
||
<meta property="og:title" content="<%= meta_title_html %>" />
|
||
<meta property="og:site_name" content="<%= meta_name_html %>" />
|
||
<meta property="og:description" content="<%= meta_description_html %>" />
|
||
<meta property="og:type" content="website" />
|
||
<meta property="og:url" content="<%= canonical_html %>" />
|
||
<meta property="og:image" content="<%= logo_url_html %>" />
|
||
<meta property="og:image:alt" content="<%= logo_alt_html %>" />
|
||
<meta name="twitter:card" content="summary" />
|
||
<meta name="twitter:title" content="<%= meta_title_html %>" />
|
||
<meta name="twitter:description" content="<%= meta_description_html %>" />
|
||
<meta name="twitter:image" content="<%= logo_url_html %>" />
|
||
<meta name="twitter:image:alt" content="<%= logo_alt_html %>" />
|
||
<link rel="icon" type="image/x-icon" href="/favicon.ico" />
|
||
<link rel="icon" type="image/svg+xml" href="/potatomesh-logo.svg" />
|
||
<link rel="stylesheet" href="/assets/styles/base.css" />
|
||
<script src="/assets/js/theme.js" defer></script>
|
||
<script src="/assets/js/background.js" defer></script>
|
||
|
||
<!-- Leaflet CSS/JS (CDN) --> <!-- Leaflet CSS/JS (CDN) -->
|
||
<link
|
||
rel="stylesheet"
|
||
href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css"
|
||
integrity="sha256-p4NxAoJBhIIN+hmNHrzRCf9tD/miZyoHS5obTRR9BMY="
|
||
crossorigin=""
|
||
/>
|
||
<script
|
||
src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"
|
||
integrity="sha256-20nQCchB9co0qIjJZRGuk2/Z9VM+kNiyxNV1lvTlZBo="
|
||
crossorigin=""
|
||
></script>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
</head>
|
||
<body data-app-config="<%= Rack::Utils.escape_html(app_config_json) %>">
|
||
<h1 class="site-title">
|
||
<img src="/potatomesh-logo.svg" alt="" aria-hidden="true" />
|
||
<span class="site-title-text"><%= site_name %></span>
|
||
</h1>
|
||
<div class="row meta">
|
||
<div class="meta-info">
|
||
<div class="refresh-row">
|
||
<p id="refreshInfo" class="refresh-info" aria-live="polite"><%= default_channel %> (<%= default_frequency %>) — active nodes: …</p>
|
||
<div class="refresh-actions">
|
||
<label class="auto-refresh-toggle"><input type="checkbox" id="autoRefresh" checked /> Auto-refresh every <%= refresh_interval_seconds %> seconds</label>
|
||
<button id="refreshBtn" type="button">Refresh now</button>
|
||
<span id="status" class="pill">loading…</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="controls">
|
||
<label><input type="checkbox" id="fitBounds" checked /> Auto-fit map</label>
|
||
<div class="filter-input">
|
||
<input type="text" id="filterInput" placeholder="Filter nodes" />
|
||
<button type="button" id="filterClear" class="filter-clear" aria-label="Clear filter" hidden>×</button>
|
||
</div>
|
||
<button id="themeToggle" type="button" aria-label="Toggle dark mode">🌙</button>
|
||
<button id="infoBtn" type="button" aria-haspopup="dialog" aria-controls="infoOverlay" aria-label="Show site information">ℹ️ Info</button>
|
||
</div>
|
||
</div>
|
||
|
||
<div id="infoOverlay" class="info-overlay" role="dialog" aria-modal="true" aria-labelledby="infoTitle" hidden>
|
||
<div class="info-dialog" tabindex="-1">
|
||
<button type="button" class="info-close" id="infoClose" aria-label="Close site information">×</button>
|
||
<h2 id="infoTitle" class="info-title">About <%= site_name %></h2>
|
||
<p class="info-intro">Quick facts about this PotatoMesh instance.</p>
|
||
<dl class="info-details">
|
||
<dt>Default channel</dt>
|
||
<dd><%= default_channel %></dd>
|
||
<dt>Frequency</dt>
|
||
<dd><%= default_frequency %></dd>
|
||
<dt>Map center</dt>
|
||
<dd><%= format("%.5f, %.5f", map_center_lat, map_center_lon) %></dd>
|
||
<dt>Visible range</dt>
|
||
<dd>Nodes within roughly <%= max_node_distance_km %> km of the center are shown.</dd>
|
||
<dt>Auto-refresh</dt>
|
||
<dd>Updates every <%= refresh_interval_seconds %> seconds.</dd>
|
||
<% if matrix_room && !matrix_room.empty? %>
|
||
<dt>Matrix room</dt>
|
||
<dd><a href="https://matrix.to/#/<%= matrix_room %>" target="_blank" rel="noreferrer noopener"><%= matrix_room %></a></dd>
|
||
<% end %>
|
||
</dl>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="map-row">
|
||
<% unless private_mode %>
|
||
<div id="chat" aria-label="Chat log"></div>
|
||
<% end %>
|
||
<div id="map" role="region" aria-label="Nodes map"></div>
|
||
</div>
|
||
|
||
<table id="nodes">
|
||
<thead>
|
||
<tr>
|
||
<th><button type="button" class="sort-button" data-sort-key="node_id" data-sort-label="Node ID">Node ID <span class="sort-indicator" aria-hidden="true"></span></button></th>
|
||
<th><button type="button" class="sort-button" data-sort-key="short_name" data-sort-label="Short Name">Short <span class="sort-indicator" aria-hidden="true"></span></button></th>
|
||
<th><button type="button" class="sort-button" data-sort-key="long_name" data-sort-label="Long Name">Long Name <span class="sort-indicator" aria-hidden="true"></span></button></th>
|
||
<th><button type="button" class="sort-button" data-sort-key="last_heard" data-sort-label="Last Seen">Last Seen <span class="sort-indicator" aria-hidden="true"></span></button></th>
|
||
<th><button type="button" class="sort-button" data-sort-key="role" data-sort-label="Role">Role <span class="sort-indicator" aria-hidden="true"></span></button></th>
|
||
<th><button type="button" class="sort-button" data-sort-key="hw_model" data-sort-label="Hardware Model">HW Model <span class="sort-indicator" aria-hidden="true"></span></button></th>
|
||
<th><button type="button" class="sort-button" data-sort-key="battery_level" data-sort-label="Battery Level">Battery <span class="sort-indicator" aria-hidden="true"></span></button></th>
|
||
<th><button type="button" class="sort-button" data-sort-key="voltage" data-sort-label="Voltage">Voltage <span class="sort-indicator" aria-hidden="true"></span></button></th>
|
||
<th><button type="button" class="sort-button" data-sort-key="uptime_seconds" data-sort-label="Uptime">Uptime <span class="sort-indicator" aria-hidden="true"></span></button></th>
|
||
<th><button type="button" class="sort-button" data-sort-key="channel_utilization" data-sort-label="Channel Utilization">Channel Util <span class="sort-indicator" aria-hidden="true"></span></button></th>
|
||
<th><button type="button" class="sort-button" data-sort-key="air_util_tx" data-sort-label="Air Utilization (Tx)">Air Util Tx <span class="sort-indicator" aria-hidden="true"></span></button></th>
|
||
<th><button type="button" class="sort-button" data-sort-key="temperature" data-sort-label="Temperature">Temperature <span class="sort-indicator" aria-hidden="true"></span></button></th>
|
||
<th><button type="button" class="sort-button" data-sort-key="relative_humidity" data-sort-label="Humidity">Humidity <span class="sort-indicator" aria-hidden="true"></span></button></th>
|
||
<th><button type="button" class="sort-button" data-sort-key="barometric_pressure" data-sort-label="Barometric Pressure">Pressure <span class="sort-indicator" aria-hidden="true"></span></button></th>
|
||
<th><button type="button" class="sort-button" data-sort-key="latitude" data-sort-label="Latitude">Latitude <span class="sort-indicator" aria-hidden="true"></span></button></th>
|
||
<th><button type="button" class="sort-button" data-sort-key="longitude" data-sort-label="Longitude">Longitude <span class="sort-indicator" aria-hidden="true"></span></button></th>
|
||
<th><button type="button" class="sort-button" data-sort-key="altitude" data-sort-label="Altitude">Altitude <span class="sort-indicator" aria-hidden="true"></span></button></th>
|
||
<th><button type="button" class="sort-button" data-sort-key="position_time" data-sort-label="Last Position">Last Position <span class="sort-indicator" aria-hidden="true"></span></button></th>
|
||
</tr>
|
||
</thead>
|
||
<tbody></tbody>
|
||
</table>
|
||
|
||
<div id="shortInfoOverlay" class="short-info-overlay" role="dialog" hidden>
|
||
<button type="button" class="short-info-close" aria-label="Close node details">×</button>
|
||
<div class="short-info-content"></div>
|
||
</div>
|
||
|
||
<footer>
|
||
PotatoMesh
|
||
<% if version && !version.empty? %>
|
||
<span class="mono"><%= version %></span> —
|
||
<% end %>
|
||
GitHub: <a href="https://github.com/l5yth/potato-mesh" target="_blank">l5yth/potato-mesh</a>
|
||
<% if matrix_room && !matrix_room.empty? %>
|
||
— <%= site_name %> Matrix:
|
||
<a href="https://matrix.to/#/<%= matrix_room %>" target="_blank"><%= matrix_room %></a>
|
||
<% end %>
|
||
</footer>
|
||
|
||
|
||
|
||
<script>
|
||
const CHAT_ENABLED = <%= private_mode ? "false" : "true" %>;
|
||
</script>
|
||
<script type="module" src="/assets/js/app/index.js"></script>
|
||
</body>
|
||
</html>
|