diff --git a/app/static/css/style.css b/app/static/css/style.css
index 97e50e3..102b0e1 100644
--- a/app/static/css/style.css
+++ b/app/static/css/style.css
@@ -472,6 +472,17 @@ main {
/* Per-hop hash size in the route line: 1 byte collides easily on a big mesh
(least desirable), 3 bytes is the best. Tokens per theme in theme.css. */
+/* One icon-labelled fact: never break between the icon and its value, or
+ inside "(5) 2 bytes" — a mid-fact wrap is what made the line hard to read */
+.message-meta .meta-fact {
+ white-space: nowrap;
+}
+
+.message-meta .meta-fact .bi {
+ margin-right: 0.25rem;
+ opacity: 0.8;
+}
+
.path-hash { font-weight: 600; } /* colour alone was too faint at this size on the light theme */
.path-hash-1 { color: var(--path-hash-1); }
.path-hash-2 { color: var(--path-hash-2); }
diff --git a/app/static/js/app.js b/app/static/js/app.js
index 10978d0..e2310fb 100644
--- a/app/static/js/app.js
+++ b/app/static/js/app.js
@@ -1535,11 +1535,22 @@ function buildEchoPaths(src) {
return paths;
}
+/** One "icon + value" fact of the meta line, with the label it replaces as tooltip. */
+function metaFact(iconClass, valueHtml, titleHtml) {
+ return `${valueHtml}`;
+}
+
/**
- * Build the "SNR | Hops | Route | Region" line shown under an incoming
+ * Build the "SNR | hops | route | region" line shown under an incoming
* channel message. `src` is a /api/messages row or a /api/messages/meta
* entry (same field names); `paths` comes from buildEchoPaths().
*
+ * Everything but the SNR is labelled by an icon rather than a word: spelled
+ * out, the line wrapped onto a second row on a phone. The icons follow the
+ * rest of the UI \u2014 bi-signpost-split is the project's path icon and
+ * bi-pin-map the status bar's region indicator \u2014 and each carries the word
+ * it replaces in its tooltip.
+ *
* SNR and hops describe the copy the node decoded and arrive with the
* message itself, so they show even when no echo was heard \u2014 a hop count
* at a glance says whether the popup is worth opening. The route part names
@@ -1555,26 +1566,32 @@ function buildMessageMetaInfo(src, paths) {
const displaySnr = (src.snr !== undefined && src.snr !== null) ? src.snr
: (src.echo_snrs && src.echo_snrs.length > 0) ? src.echo_snrs[0] : null;
if (typeof displaySnr === 'number') {
- metaParts.push(`SNR: ${displaySnr.toFixed(1)} dB`);
+ metaParts.push(`SNR: ${displaySnr.toFixed(1)} dB`);
}
const hopCount = src.hop_count ?? (src.path_len !== null && src.path_len !== undefined ? (src.path_len & 0x3F) : null);
if (hopCount !== null && hopCount !== undefined) {
- metaParts.push(tHtml('chat.route_hops', { count: hopCount }));
+ metaParts.push(metaFact('bi-broadcast-pin', hopCount,
+ tHtml('chat.route_hops', { count: hopCount })));
}
if (paths && paths.length > 0) {
const hashSize = paths[0].hash_size || 1;
- // The translated string is escaped as a whole, so the coloured span is
- // spliced in afterwards through a placeholder the catalog never contains.
- const TOKEN = '%%ROUTE%%';
- const sizeHtml = `${escapeHtml(tn('chat.route_hash_bytes', hashSize))}`;
- const routeText = (paths.length > 1
- ? tHtml('chat.route_multi', { count: paths.length, route: TOKEN })
- : tHtml('chat.route', { route: TOKEN })).replace(TOKEN, sizeHtml);
+ // Shown as "2B" — the SI/IEC symbol, upper case for byte (lower case
+ // is bit). The words stay in the tooltip, where there is room.
+ const sizeShort = tHtml('chat.route_hash_bytes_short', { count: hashSize });
+ const sizeLabel = tn('chat.route_hash_bytes', hashSize);
+ // Copies heard, shown only when there is more than one \u2014 as before,
+ // no count means the message was heard once.
+ const countHtml = paths.length > 1 ? `(${paths.length}) ` : '';
+ const title = (paths.length > 1
+ ? tHtml('chat.route_multi', { count: paths.length, route: sizeLabel })
+ : tHtml('chat.route', { route: sizeLabel })) + ' \u2014 ' + tHtml('chat.route_hash_title');
+ const value = `${countHtml}${sizeShort}`;
const pathsData = encodeURIComponent(JSON.stringify(paths));
- metaParts.push(`${routeText}`);
+ metaParts.push(`${value}`);
}
if (src.region) {
- metaParts.push(tHtml('chat.route_region', { name: src.region }));
+ metaParts.push(metaFact('bi-pin-map', escapeHtml(src.region),
+ tHtml('chat.route_region', { name: src.region })));
}
return metaParts.join(' | ');
}
diff --git a/app/translations/en.json b/app/translations/en.json
index ea5b746..fdf6bc3 100644
--- a/app/translations/en.json
+++ b/app/translations/en.json
@@ -155,6 +155,7 @@
"one": "{count} byte",
"other": "{count} bytes"
},
+ "chat.route_hash_bytes_short": "{count}B",
"chat.route_hash_title": "Per-hop hash size of the route. Tap to list every route this message was heard on",
"chat.route_hops": "Hops: {count}",
"chat.route_multi": "Route ({count}): {route}",
diff --git a/app/translations/pl.json b/app/translations/pl.json
index 94a9885..882fb27 100644
--- a/app/translations/pl.json
+++ b/app/translations/pl.json
@@ -159,6 +159,7 @@
"many": "{count} bajtów",
"other": "{count} bajtu"
},
+ "chat.route_hash_bytes_short": "{count}B",
"chat.route_hash_title": "Rozmiar hasha na hop w trasie. Dotknij, aby zobaczyć wszystkie trasy, którymi dotarła ta wiadomość",
"chat.route_hops": "Hopy: {count}",
"chat.route_multi": "Trasa ({count}): {route}",
diff --git a/docs/user-guide.md b/docs/user-guide.md
index 315c6bc..ac70561 100644
--- a/docs/user-guide.md
+++ b/docs/user-guide.md
@@ -213,7 +213,9 @@ Your own messages carry a small row of action buttons:
### Group Chat Message Routes
-Under every incoming channel message a line shows the **SNR** and **hop count** of the copy your node decoded — they arrive with the message itself, so they are there even when nothing else was heard. When your node also heard the message being repeated, a **Route** part follows, saying how many copies were heard and how wide their per-hop hashes are, e.g. `SNR: 9.3 dB | Hops: 5 | Route (3): 2 bytes`. The hash size is coloured: **red** for 1 byte (such hashes collide easily on a big mesh), **orange** for 2 bytes and **green** for 3 bytes. Tap the Route part to open a popup listing every copy in the order they were heard, each with its route, SNR and hop count:
+Under every incoming channel message a line shows the **SNR** and **hop count** of the copy your node decoded — they arrive with the message itself, so they are there even when nothing else was heard. When your node also heard the message being repeated, a route part follows, saying how many copies were heard and how wide their per-hop hashes are — written as `2B`, meaning 2 bytes per hop. The size is coloured: **red** for 1 byte (such hashes collide easily on a big mesh), **orange** for 2 bytes and **green** for 3 bytes.
+
+Apart from the SNR, each value is labelled by a small icon rather than a word, so the line fits one row on a phone — an antenna 📡 for the hop count, a signpost for the routes and a map pin for the region. Hovering an icon (or a long press on a phone) names it. Tap the signpost part to open a popup listing every copy in the order they were heard, each with its route, SNR and hop count:
- The copy your node actually decoded is marked **✓ received**; the others arrived later and were dropped as duplicates, but they still show where else the message travelled. A copy heard straight from the sender, with no repeater in between, is listed as **Direct** with 0 hops.
- Tap a route to open the **[Path Analyzer](#path-analyzer)** on the Map view, with that message selected and that exact route drawn — the quickest way to see where a message physically travelled.
diff --git a/docs/whatsnew.md b/docs/whatsnew.md
index d205b03..ab713c3 100644
--- a/docs/whatsnew.md
+++ b/docs/whatsnew.md
@@ -12,7 +12,7 @@ For deep technical notes, see [architecture.md](architecture.md). For the full g
### Features
-- **The line under an incoming channel message now names the region it was sent in, and no longer spells out the route.** A MeshCore sender can stamp a packet with a *region* — a flood scope such as `pl` that only repeaters allowing it pass on (see the previous release). The stamp is not the name but a 16-bit code derived from the region's key, so the interface can recognise it only for regions you have added under **Settings → Channels**: a message stamped with one of them now ends its line with **Region: pl**, while an unstamped message, or one from a region you have not configured, shows nothing extra. The route part changed too. It used to print the first route inline (`SNR: -5.5 dB | Hops: 4 | Route (3): 142C→1122→271C→EE6C`), which with 2- or 3-byte hashes crowded everything else off a phone screen. It now only says how many copies of the message were heard and how wide their per-hop hashes are, `Route (3): 2 bytes`, with the size coloured **red**, **orange** or **green** for 1, 2 or 3 bytes, and tapping it opens the same popup as before. The SNR and hop count stay where they were: they describe the copy your node decoded, arrive with the message itself, and so are there even when no repeat was heard. The popup is now complete: it lists every copy in the order heard, including one that came **Direct** from the sender with no repeater in between (previously left out, which made "Hops: 0" under a message look wrong next to popup entries with one hop), and marks the copy your node actually decoded with **✓ received**. The whole line is also a size larger and darker, since it was hard to read on a big screen. The region stamp is kept from this version on, so messages received before the update have no region to show.
+- **The line under an incoming channel message now names the region it was sent in, and no longer spells out the route.** A MeshCore sender can stamp a packet with a *region* — a flood scope such as `pl` that only repeaters allowing it pass on (see the previous release). The stamp is not the name but a 16-bit code derived from the region's key, so the interface can recognise it only for regions you have added under **Settings → Channels**: a message stamped with one of them now ends its line with **Region: pl**, while an unstamped message, or one from a region you have not configured, shows nothing extra. The route part changed too. It used to print the first route inline (`SNR: -5.5 dB | Hops: 4 | Route (3): 142C→1122→271C→EE6C`), which with 2- or 3-byte hashes crowded everything else off a phone screen. It now only says how many copies of the message were heard and how wide their per-hop hashes are — `(3) 2B`, three copies with 2-byte hashes — with the size coloured **red**, **orange** or **green** for 1, 2 or 3 bytes, and tapping it opens the same popup as before. The words *Hops*, *Route* and *Region* are gone as well, replaced by a small antenna, signpost and map-pin icon, each naming itself when hovered or long-pressed: spelled out, the line took two rows on a phone and pushed the messages apart. The SNR and hop count stay where they were: they describe the copy your node decoded, arrive with the message itself, and so are there even when no repeat was heard. The popup is now complete: it lists every copy in the order heard, including one that came **Direct** from the sender with no repeater in between (previously left out, which made "Hops: 0" under a message look wrong next to popup entries with one hop), and marks the copy your node actually decoded with **✓ received**. The whole line is also a size larger and darker, since it was hard to read on a big screen. The region stamp is kept from this version on, so messages received before the update have no region to show.
---