diff --git a/meshview/static/kiosk.html b/meshview/static/kiosk.html new file mode 100644 index 0000000..0c2bfa0 --- /dev/null +++ b/meshview/static/kiosk.html @@ -0,0 +1,225 @@ + + + + +Mesh Nodes Live Map + + + + + +
+ +
+
+
+ + + + + + + + diff --git a/meshview/templates/base.html b/meshview/templates/base.html index c14e2ad..eb741a5 100644 --- a/meshview/templates/base.html +++ b/meshview/templates/base.html @@ -1,5 +1,5 @@ - + Meshview - {{ site_config.get("site", {}).get("title", "") }} @@ -9,32 +9,22 @@ <meta name="viewport" content="width=device-width, initial-scale=1"> <!-- Scripts --> - <script src="https://unpkg.com/htmx.org@1.9.11" integrity="sha384-0gxUXCCR8yv9FM2b+U3FDbsKthCI66oH5IA9fHppQq9DDMHuMauqq1ZHBpJxQ0J0" crossorigin="anonymous"></script> + <script src="https://unpkg.com/htmx.org@1.9.11" crossorigin="anonymous"></script> <script src="https://unpkg.com/htmx.org@1.9.11/dist/ext/sse.js" crossorigin="anonymous"></script> - <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz" crossorigin="anonymous"></script> + <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js" crossorigin="anonymous"></script> <!-- Stylesheets --> - <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous"> - <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> + <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" crossorigin="anonymous"> + <link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" crossorigin=""/> + <script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js" crossorigin=""></script> {% block head %}{% endblock %} <style> - .htmx-indicator { - opacity: 0; - transition: opacity 500ms ease-in; - } - .htmx-request .htmx-indicator { - opacity: 1; - } - #search_form { - z-index: 4000; - } - #details_map { - width: 100%; - height: 500px; - } + .htmx-indicator { opacity: 0; transition: opacity 500ms ease-in; } + .htmx-request .htmx-indicator { opacity: 1; } + #search_form { z-index: 4000; } + #details_map { width: 100%; height: 500px; } {% block css %}{% endblock %} </style> </head> @@ -45,30 +35,105 @@ <div style="text-align:center"> <strong>{{ site.get("title", "") }} {{ site.get("domain", "") }}</strong> </div> - <div style="text-align: center;"> + <div style="text-align:center"> {{ site.get("message", "") }} </div> + <!-- Menu --> <div style="text-align:center"> - {% if site.get("nodes") == "True" %}<a href="/nodelist">Nodes</a>{% endif %} - {% if site.get("conversations") == "True" %} - <a href="/chat">Conversations</a>{% endif %} - {% if site.get("everything") == "True" %} - <a href="/firehose">See <strong>everything</strong></a>{% endif %} - {% if site.get("graphs") == "True" %} - <a href="/nodegraph">Mesh Graphs</a>{% endif %} - {% if site.get("net") == "True" %} - <a href="/net">Weekly Net</a>{% endif %} - {% if site.get("map") == "True" %} - <a href="/map">Live Map</a>{% endif %} - {% if site.get("stats") == "True" %} - <a href="/stats">Stats</a>{% endif %} - {% if site.get("top") == "True" %} - <a href="/top">Top Traffic</a>{% endif %} + {% if site.get("nodes") == "True" %}<a href="/nodelist" id="nav-nodes" data-translate-lang="nodes">Nodes</a>{% endif %} + {% if site.get("conversations") == "True" %} - <a href="/chat" id="nav-conversations" data-translate-lang="conversations">Conversations</a>{% endif %} + {% if site.get("everything") == "True" %} - <a href="/firehose" id="nav-everything" data-translate-lang="everything">See Everything</a>{% endif %} + {% if site.get("graphs") == "True" %} - <a href="/nodegraph" id="nav-graph" data-translate-lang="graph">Mesh Graphs</a>{% endif %} + {% if site.get("net") == "True" %} - <a href="/net" id="nav-net" data-translate-lang="net">Weekly Net</a>{% endif %} + {% if site.get("map") == "True" %} - <a href="/map" id="nav-map" data-translate-lang="map">Live Map</a>{% endif %} + {% if site.get("stats") == "True" %} - <a href="/stats" id="nav-stats" data-translate-lang="stats">Stats</a>{% endif %} + {% if site.get("top") == "True" %} - <a href="/top" id="nav-top" data-translate-lang="top">Top Traffic Nodes</a>{% endif %} </div> - {% include "search_form.html" %} + <!-- Search Form --> + <form class="container p-2 sticky-top mx-auto" id="search_form" action="/node_search"> + <div class="row"> + <input + class="col m-2" + id="q" + type="text" + name="q" + data-translate-lang="node id" + placeholder="Node id" + autocomplete="off" + list="node_options" + value="{{raw_node_id}}" + hx-trigger="input delay:100ms" + hx-get="/node_match" + hx-target="#node_options" + /> + <datalist id="node_options"> + {% for option in node_options %} + <option value="{{option.id}}">{{option.id}} -- {{option.long_name}} ({{option.short_name}})</option> + {% endfor %} + </datalist> + + <select name="portnum" class="col-2 m-2" id="portnum_select"> + <!-- Options will be populated dynamically --> + </select> + + <input type="submit" value="Go to Node" class="col-2 m-2" data-translate-lang="go to node" /> + </div> + </form> {% block body %}{% endblock %} <br> - <div style="text-align:center"> - Visit <strong><a href="https://github.com/pablorevilla-meshtastic/meshview">Meshview</a></strong> on Github. - <small>ver. {{ SOFTWARE_RELEASE | default("unknown") }}</small> - </div> + <div style="text-align:center" id="footer" data-translate-lang="footer"> + </div><div style="text-align:center"><div><small>ver. {{ SOFTWARE_RELEASE | default("unknown") }}</small></div> <br> + + <!-- Language Loader --> + <script> + async function loadTranslations() { + try { + const langCode = "{{ site_config.get('site', {}).get('language', 'en') }}"; + const res = await fetch(`/api/lang?lang=${langCode}§ion=base`); + const t = await res.json(); + + document.querySelectorAll("[data-translate-lang]").forEach(el => { + const key = el.getAttribute("data-translate-lang"); + if (t[key]) { + if (el.placeholder !== undefined && el.tagName === "INPUT" && el.type === "text") { + el.placeholder = t[key]; + } else if (el.tagName === "INPUT" && el.type === "submit") { + el.value = t[key]; + } else { + el.innerHTML = t[key]; + } + } + }); + + // Portnum options + const select = document.getElementById("portnum_select"); + if (select && t.portnum_options) { + select.innerHTML = ""; // Clear + const allOption = document.createElement("option"); + allOption.value = ""; + allOption.textContent = t["all"] || "All"; + if ("{{portnum}}" === "") allOption.selected = true; + select.appendChild(allOption); + + for (const [value, label] of Object.entries(t.portnum_options)) { + const opt = document.createElement("option"); + opt.value = value; + opt.textContent = label; + if ("{{portnum}}" === String(value)) opt.selected = true; + select.appendChild(opt); + } + } + } catch (err) { + console.error("Failed to load language:", err); + } + } + + document.addEventListener("DOMContentLoaded", loadTranslations); + </script> </body> </html>