diff --git a/frontend/index.html b/frontend/index.html index d03f95a..4847cf0 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -38,6 +38,7 @@ --tr: #22d3ee; --envelope: #fbbf24; --auth: #f87171; + --pair-row: rgba(148, 163, 184, 0.12); } [data-theme="light"] { --bg: #fafafa; @@ -68,6 +69,7 @@ --tr: #0e7490; --envelope: #b45309; --auth: #dc2626; + --pair-row: rgba(100, 116, 139, 0.15); } * { margin: 0; padding: 0; box-sizing: border-box; } @@ -114,6 +116,7 @@ body { font: 14px/1.5 system-ui, sans-serif; background: var(--bg); color: var(- /* APDU list */ .apdu-list { flex: 1; overflow-y: auto; padding: 8px; } .apdu-entry { display: flex; gap: 12px; padding: 3px 12px; border-bottom: 1px solid var(--bg-panel); cursor: pointer; align-items: baseline; } +.apdu-entry.apdu-pair { background: var(--pair-row); } .apdu-entry:hover { background: var(--bg-panel); } .apdu-entry.selected { background: var(--bg-input); } [data-theme="light"] .apdu-entry.selected { background: #dbeafe; } @@ -125,6 +128,7 @@ body { font: 14px/1.5 system-ui, sans-serif; background: var(--bg); color: var(- .apdu-type.cat-tr { font-weight: 700; color: var(--tr); } .apdu-type.cat-envelope { color: var(--envelope); } .apdu-type.cat-auth { color: var(--auth); } +.apdu-type.cat-gr { color: var(--text-muted); font-weight: 600; } .apdu-data { font-family: monospace; color: var(--text-dim); word-break: break-all; flex: 1; } .apdu-sw { font-family: monospace; color: var(--text-muted); white-space: nowrap; min-width: 52px; } .apdu-sw.sw-ok { color: var(--green); } @@ -1084,6 +1088,7 @@ function command_cat(ins) { if (ins === '14' || ins === '10' || ins === 'aa') return 'tr'; // TERMINAL RESPONSE / PROFILE / CAPABILITY if (ins === 'c2') return 'envelope'; // ENVELOPE if (ins === '88' || ins === '89') return 'auth'; // AUTHENTICATE + if (ins === 'c0') return 'gr'; // GET RESPONSE return null; } @@ -1349,9 +1354,16 @@ function renderMessages(skipScroll) { (cat ? ' cat-' + cat : ''); const errs = error_names(m); const errBadge = errs ? `⚠` : ''; - return `
+ // Command + GET RESPONSE pairs: subtle tint on both members, + // ↳ prefix marks the response as a continuation of the previous + // command (server sets response_for when prior SW1 was '61'). + const isGR = m.type === 'tpdu' && m.decoded && + m.decoded.ins_name === 'GET RESPONSE' && m.decoded.response_for; + const pairCls = (isGR || isOriginalCommand(idx)) ? ' apdu-pair' : ''; + const grLabel = isGR ? '↳ ' + label : label; + return `
${fmt_elapsed(m.elapsed)} - ${label} + ${grLabel} ${sw_text(m)} ${cmd_description(m)} ${errBadge} @@ -1869,7 +1881,7 @@ function onFilterChange() { } // ──────────────────── Versioning ──────────────────── -const PWA_VERSION = '1.14.0'; +const PWA_VERSION = '1.15.0'; const MIN_SERVER_VERSION = '1.0.0'; function parseVersion(v) { diff --git a/frontend/sw.js b/frontend/sw.js index 1bc7832..0c16157 100644 --- a/frontend/sw.js +++ b/frontend/sw.js @@ -1,4 +1,4 @@ -const CACHE = 'simtrace-analyser-v17'; +const CACHE = 'simtrace-analyser-v20'; const PRECACHE = [ 'index.html', diff --git a/pyproject.toml b/pyproject.toml index c1207db..882998e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.backends._legacy:_Backend" [project] name = "simtrace2-pysniff" -version = "1.14.0" +version = "1.15.0" description = "Python-based sniffing tool for SIMtrace2 hardware (SIM card communication sniffer)" readme = {file = "README.md", content-type = "text/markdown"} license = {text = "GPL-2.0-or-later"} diff --git a/simtrace2_pysniff/__init__.py b/simtrace2_pysniff/__init__.py index 83c795f..4c6bc5f 100644 --- a/simtrace2_pysniff/__init__.py +++ b/simtrace2_pysniff/__init__.py @@ -6,7 +6,7 @@ from .gsmtap import GsmtapSender, GsmtapReceiver from .pcap import PcapWriter from .dump import FileDumper, format_message -__version__ = '1.14.0' +__version__ = '1.15.0' __all__ = [ 'SniffSession', 'DeviceDisconnected', diff --git a/tests/test_pcap_import.py b/tests/test_pcap_import.py index d42cde1..2dc33f9 100644 --- a/tests/test_pcap_import.py +++ b/tests/test_pcap_import.py @@ -197,7 +197,7 @@ class TestImportEndpoint(unittest.TestCase): class TestVersion(unittest.TestCase): def test_package_version(self): from simtrace2_pysniff import __version__ - self.assertEqual(__version__, '1.14.0') + self.assertEqual(__version__, '1.15.0') def test_status_includes_version(self): import os