diff --git a/data_embed/index.html b/data_embed/index.html
index 1020495..6ca4ac9 100644
--- a/data_embed/index.html
+++ b/data_embed/index.html
@@ -62,6 +62,12 @@ body{background:var(--msh-bg);color:var(--msh-text);font-family:"Inter",system-u
#received-packets .table{--bs-table-color:var(--msh-text);--bs-table-border-color:var(--msh-border);color:var(--msh-text);}
#received-packets th{white-space:nowrap;}
#received-packets td:nth-child(1),#received-packets td:nth-child(2),#received-packets td:nth-child(4),#received-packets td:nth-child(5){white-space:nowrap;}
+#received-packets th:nth-child(1){width:calc(10ch + 1rem);min-width:calc(10ch + 1rem);}
+#received-packets th:nth-child(2){width:calc(8ch + 1rem);min-width:calc(8ch + 1rem);}
+#received-packets th:nth-child(4){width:calc(4ch + 1rem);min-width:calc(4ch + 1rem);}
+#received-packets th:nth-child(5){width:calc(6ch + 1rem);min-width:calc(6ch + 1rem);}
+#received-packets td:nth-child(1),#received-packets td:nth-child(2),#received-packets td:nth-child(4),#received-packets td:nth-child(5){font-variant-numeric:tabular-nums;}
+#received-packets td:nth-child(3){overflow-wrap:anywhere;}
@media(max-width:768px){.app-sidebar{width:60px;padding:12px 6px;}.side-link .label,.nav-group-label{display:none;}.app-main{padding:14px;}}
@@ -198,7 +204,7 @@ body{background:var(--msh-bg);color:var(--msh-text);font-family:"Inter",system-u
| Date |
Time |
- Frame |
+ Packet |
RSSI |
SNR |
diff --git a/data_embed/script.js b/data_embed/script.js
index a4a90ae..98cdfcf 100644
--- a/data_embed/script.js
+++ b/data_embed/script.js
@@ -653,6 +653,15 @@ let mapTileErrShown = false;
let iGateLatLng = null;
let mapAutoFitDone = false;
+function escapeHtml(value) { // shows received data as text inside HTML strings (map popups / icons)
+ return String(value ?? "")
+ .replace(/&/g, "&")
+ .replace(//g, ">")
+ .replace(/"/g, """)
+ .replace(/'/g, "'");
+}
+
function setMapMessage(text) {
const el = document.getElementById("map");
if (el) {
@@ -831,7 +840,7 @@ function getAprsSymbolIcon(symbol, callsign = "") {
-1px 1px 0 #fff,
1px 1px 0 #fff;
pointer-events:none;
- ">${callsign}
+ ">${escapeHtml(callsign)}
`,
iconSize: [24, 24],
@@ -933,12 +942,12 @@ function loadMapStations(stations) {
(stations || []).forEach((s) => {
if (s.lat === 0 && s.lon === 0) return; // descartar estaciones sin fix
- const popup = `${s.callsign}`
- + (s.lastHeard ? `
Last: ${s.lastHeard}` : "")
+ const popup = `${escapeHtml(s.callsign)}`
+ + (s.lastHeard ? `
Last: ${escapeHtml(s.lastHeard)}` : "")
+ `
RSSI ${s.RSSI} / SNR ${s.SNR}`
+ `
Packets: ${s.count}`
- + `
${describeMapPath(s)}`
- + (s.path ? `
Path: ${s.path}` : "");
+ + `
${escapeHtml(describeMapPath(s))}`
+ + (s.path ? `
Path: ${escapeHtml(s.path)}` : "");
const aprsIcon =
getAprsSymbolIcon(s.symbol, s.callsign);
diff --git a/src/LoRa_APRS_iGate.cpp b/src/LoRa_APRS_iGate.cpp
index b96343b..37a4b19 100644
--- a/src/LoRa_APRS_iGate.cpp
+++ b/src/LoRa_APRS_iGate.cpp
@@ -68,7 +68,7 @@ ___________________________________________________________________*/
#endif
-String versionDate = "2026-10-01";
+String versionDate = "2026-10-02";
String versionNumber = "4.0.2";
Configuration Config;
WiFiClient aprsIsClient;