diff --git a/meshview/__version__.py b/meshview/__version__.py
index de9f5a7..6f7f517 100644
--- a/meshview/__version__.py
+++ b/meshview/__version__.py
@@ -3,8 +3,8 @@
import subprocess
from pathlib import Path
-__version__ = "3.0.5"
-__release_date__ = "2026-2-6"
+__version__ = "3.0.6"
+__release_date__ = "2026-3-6"
def get_git_revision():
diff --git a/meshview/templates/base.html b/meshview/templates/base.html
index 44f3d0d..80447ab 100644
--- a/meshview/templates/base.html
+++ b/meshview/templates/base.html
@@ -176,6 +176,7 @@ async function initializePage() {
items.push(`${dict[key] || key}`);
}
}
+ items.push('MeshviewWorld');
menu.innerHTML = items.join(" - ");
}
diff --git a/meshview/templates/map.html b/meshview/templates/map.html
index f9832af..1caf633 100644
--- a/meshview/templates/map.html
+++ b/meshview/templates/map.html
@@ -23,7 +23,25 @@
#reset-filters-button:hover { background-color:#da190b; }
#reset-filters-button:active { background-color:#c41e0d; }
- .blinking-tooltip { background:white;color:black;border:1px solid black;border-radius:4px;padding:2px 5px; }
+ .blinking-tooltip {
+ background: white;
+ color: black;
+ border: 2px solid #111;
+ border-radius: 6px;
+ padding: 6px 10px;
+ font-size: 14px;
+ font-weight: 600;
+ box-shadow: 0 2px 8px rgba(0,0,0,0.25);
+ }
+ .blinking-tooltip.text-packet {
+ animation: textPulse 1.1s ease-in-out 6;
+ border-color: #ff8c00;
+ }
+ @keyframes textPulse {
+ 0% { box-shadow: 0 2px 8px rgba(0,0,0,0.25); }
+ 50% { box-shadow: 0 4px 14px rgba(255,140,0,0.45); }
+ 100% { box-shadow: 0 2px 8px rgba(0,0,0,0.25); }
+ }
#map-wrapper {
position: relative;
@@ -274,7 +292,7 @@ function fetchNewPackets(){
const marker = markerById[pkt.from_node_id];
const nodeData = nodeMap.get(pkt.from_node_id);
if(marker && nodeData) {
- blinkNode(marker,nodeData.long_name,pkt.portnum);
+ blinkNode(marker, nodeData.long_name, pkt.portnum, pkt.payload);
} else {
addUnmappedPacket(pkt, nodeData);
}
@@ -568,7 +586,20 @@ map.on('click', e=>{
BLINKING
====================================================== */
-function blinkNode(marker,longName,portnum){
+function escapeHtml(value){
+ return String(value)
+ .replace(/&/g, "&")
+ .replace(//g, ">")
+ .replace(/"/g, """)
+ .replace(/'/g, "'");
+}
+
+function isTextPort(portnum){
+ return portnum === 1 || portnum === 7;
+}
+
+function blinkNode(marker,longName,portnum,payload){
if(!map.hasLayer(marker)) return;
if(activeBlinks.has(marker)){
@@ -578,13 +609,24 @@ function blinkNode(marker,longName,portnum){
}
let blinkCount = 0;
+ const blinkStart = Date.now();
+ const blinkDurationMs = 7000;
+ const safeName = escapeHtml(longName);
+ const portLabel = portMap[portnum] || `Port ${portnum ?? "?"}`;
+ const payloadText = (payload || "").trim();
+ const showPayload = isTextPort(portnum) && payloadText && payloadText !== "Did not decode";
+ const shortPayload = showPayload && payloadText.length > 80
+ ? `${payloadText.slice(0, 77)}...`
+ : payloadText;
+ const payloadLine = showPayload ? `
${escapeHtml(shortPayload)}` : "";
+
const tooltip = L.tooltip({
permanent:true,
direction:'top',
offset:[0,-marker.options.radius-5],
- className:'blinking-tooltip'
+ className: isTextPort(portnum) ? 'blinking-tooltip text-packet' : 'blinking-tooltip'
})
- .setContent(`${longName} (${portMap[portnum] || "Port "+portnum})`)
+ .setContent(`${safeName} (${escapeHtml(portLabel)})${payloadLine}`)
.setLatLng(marker.getLatLng())
.addTo(map);
@@ -599,7 +641,7 @@ function blinkNode(marker,longName,portnum){
}
blinkCount++;
- if(blinkCount>7){
+ if(Date.now() - blinkStart > blinkDurationMs){
clearInterval(interval);
marker.setStyle({ fillColor: marker.originalColor });
map.removeLayer(tooltip);
diff --git a/meshview/templates/node.html b/meshview/templates/node.html
index 334c492..d3c8a7c 100644
--- a/meshview/templates/node.html
+++ b/meshview/templates/node.html
@@ -18,21 +18,23 @@
/* --- Node Info --- */
.node-info {
- background-color: #1f2226;
- border: 1px solid #3a3f44;
color: #ddd;
font-size: 0.88rem;
- padding: 12px 14px;
margin-bottom: 14px;
- border-radius: 8px;
-
display: grid;
grid-template-columns: repeat(3, minmax(120px, 1fr));
- grid-column-gap: 14px;
- grid-row-gap: 6px;
+ grid-column-gap: 12px;
+ grid-row-gap: 12px;
}
-.node-info div { padding: 2px 0; }
+.node-info-col {
+ background-color: #1f2226;
+ border: 1px solid #3a3f44;
+ border-radius: 8px;
+ padding: 12px 14px;
+}
+
+.node-info-col div { padding: 2px 0; }
.node-info strong {
color: #9fd4ff;
font-weight: 600;
@@ -357,31 +359,32 @@