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