diff --git a/frontend/index.html b/frontend/index.html
index 77e83d0..427cc65 100644
--- a/frontend/index.html
+++ b/frontend/index.html
@@ -2183,6 +2183,12 @@ function hexImageRows(segments, ranges, observed, limit, size) {
end = Math.max(end, seg.offset + Math.floor(hex.length / 2));
}
const covered = (list, pos) => (list || []).some(([p, n]) => pos >= p && pos < p + n);
+ // The filler stops at the known file/record length (a short file must not
+ // look like it has missing data) — and never hides observed bytes: when
+ // the FCP understates the layout (a 2-byte record_len for a 44-byte
+ // record read) the data's own end is the limit. Without a known length
+ // the row also ends at the data instead of inventing bytes.
+ const stopAt = size && size >= end ? size : end;
const rows = [];
const used = {changed: false, fresh: false, unknown: false};
let truncated = false;
@@ -2197,7 +2203,7 @@ function hexImageRows(segments, ranges, observed, limit, size) {
// Never pad past the known file/record length: the filler would
// read as missing data when the file is simply short. '--'
// still marks bytes inside the file that were never observed.
- if (size && pos >= size) break;
+ if (stopAt && pos >= stopAt) break;
const hex = bytes.get(pos);
if (hex === undefined) {
cells.push({hex: '--', cls: 'hex-unknown'});
@@ -3380,7 +3386,7 @@ function onSearchChange() {
}
// ──────────────────── Versioning ────────────────────
-const PWA_VERSION = '2.7.4';
+const PWA_VERSION = '2.7.5';
const MIN_SERVER_VERSION = '2.3.0';
function parseVersion(v) {
diff --git a/frontend/sw.js b/frontend/sw.js
index c8285eb..73bbf29 100644
--- a/frontend/sw.js
+++ b/frontend/sw.js
@@ -1,4 +1,4 @@
-const CACHE = 'simtrace-analyser-v84';
+const CACHE = 'simtrace-analyser-v85';
const PRECACHE = [
'index.html',
diff --git a/frontend/tests/comprion.test.js b/frontend/tests/comprion.test.js
index 7541c0c..722918b 100644
--- a/frontend/tests/comprion.test.js
+++ b/frontend/tests/comprion.test.js
@@ -332,9 +332,18 @@ test('Files view: the hex rows stop at the known length (no filler)', () => {
assert.equal(rec.rows[0].cells.length, 9);
assert.equal(rec.rows[0].cells.filter(c => c.cls === 'hex-unknown').length, 5);
assert.deepEqual(rec.used, {changed: true, fresh: false, unknown: true});
- // Without a known length the old row padding stays (nothing better to do).
+ // Without a known length the row ends at the data — no invented bytes.
const noSize = hexImageRows([{offset: 0, hex: 'aabb'}], [], [], undefined, null);
- assert.equal(noSize.rows[0].cells.length, 16);
+ assert.equal(noSize.rows[0].cells.length, 2);
+ // A card whose FCP understates the layout (a 2-byte record_len for a
+ // 44-byte read) must still show every observed byte, not two per row.
+ const wide = hexImageRows(
+ [{offset: 0, hex: 'aa'.repeat(44)}], [[0, 2]], [[2, 42]], undefined, 2);
+ assert.deepEqual(wide.rows.map(r => r.cells.length), [16, 16, 12]);
+ assert.equal(wide.rows[0].cells[0].cls, 'hex-changed');
+ assert.equal(wide.rows[0].cells[2].cls, 'hex-new');
+ assert.equal(wide.used.changed, true);
+ assert.equal(wide.used.fresh, true);
});
test('Files view: the hex image respects the row limit', () => {
diff --git a/pyproject.toml b/pyproject.toml
index fadff98..5d086be 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -4,7 +4,7 @@ build-backend = "setuptools.backends._legacy:_Backend"
[project]
name = "simtrace-analyser"
-version = "2.7.4"
+version = "2.7.5"
description = "SIMtrace Analyser — APDU trace analyser for GSMTAP/PCAP captures (bundles the simtrace2-pysniff utility)"
readme = {file = "README.md", content-type = "text/markdown"}
license = {text = "GPL-2.0-or-later"}
diff --git a/simtrace_analyser/version.py b/simtrace_analyser/version.py
index 77c9610..a22159c 100644
--- a/simtrace_analyser/version.py
+++ b/simtrace_analyser/version.py
@@ -1,3 +1,3 @@
"""Single source of truth for the SIMtrace Analyser version."""
-__version__ = '2.7.4'
+__version__ = '2.7.5'
diff --git a/tests/test_pcap_import.py b/tests/test_pcap_import.py
index 91f0156..46d77d0 100644
--- a/tests/test_pcap_import.py
+++ b/tests/test_pcap_import.py
@@ -224,7 +224,7 @@ class TestImportEndpoint(unittest.TestCase):
class TestVersion(unittest.TestCase):
def test_package_version(self):
from simtrace2_pysniff import __version__
- self.assertEqual(__version__, '2.7.4')
+ self.assertEqual(__version__, '2.7.5')
def test_status_includes_version(self):
import os