Add draggable splitter between APDU list and detail view

- Insert .splitter div between .apdu-list and .apdu-detail
- Replace flex:1 / max-height:40% with --detail-height CSS var
- Drag handler with clamp 10%-80%, persisted in localStorage
- Bump 1.22.12, SW cache v43->v44
This commit is contained in:
catarrh committed 2026-09-06 21:36:33 +03:00
1 parent 7d8dca1ffd
commit 52243f496c
5 files changed
+44 -7

No files matched your search

+40 -3
View File
@@ -119,7 +119,7 @@ body { font: 14px/1.5 system-ui, sans-serif; background: var(--bg); color: var(-
.trace-left { flex: 1; display: flex; flex-direction: column; overflow: hidden; } .trace-left { flex: 1; display: flex; flex-direction: column; overflow: hidden; }
/* APDU list */ /* APDU list */
.apdu-list { flex: 1; overflow-y: auto; padding: 8px; } .apdu-list { flex: 1 1 0; min-height: 0; 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 { 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.apdu-pair { background: var(--pair-row); }
.apdu-entry:hover { background: var(--bg-panel); } .apdu-entry:hover { background: var(--bg-panel); }
@@ -149,7 +149,10 @@ body { font: 14px/1.5 system-ui, sans-serif; background: var(--bg); color: var(-
.device-warn { padding: 8px 12px; font-size: 12px; color: var(--warn); background: var(--warning-bg); border-bottom: 1px solid var(--warn); } .device-warn { padding: 8px 12px; font-size: 12px; color: var(--warn); background: var(--warning-bg); border-bottom: 1px solid var(--warn); }
/* APDU detail panel */ /* APDU detail panel */
.apdu-detail { display: flex; flex-direction: column; max-height: 40%; overflow-y: auto; border-top: 2px solid var(--accent); background: var(--bg-panel); flex-shrink: 0; } .apdu-detail { display: flex; flex-direction: column; flex: 0 0 var(--detail-height, 30%); overflow-y: auto; background: var(--bg-panel); }
/* Splitter bar */
.splitter { height: 6px; cursor: row-resize; background: var(--border); flex-shrink: 0; }
.splitter:hover, .splitter.active { background: var(--accent); }
.apdu-detail .detail-empty { padding: 24px; color: var(--text-muted); text-align: center; } .apdu-detail .detail-empty { padding: 24px; color: var(--text-muted); text-align: center; }
.detail-header { display: flex; align-items: center; justify-content: space-between; padding: 8px 12px; background: var(--bg-input); border-bottom: 1px solid var(--border); } .detail-header { display: flex; align-items: center; justify-content: space-between; padding: 8px 12px; background: var(--bg-input); border-bottom: 1px solid var(--border); }
[data-theme="light"] .detail-header { background: #dbeafe; } [data-theme="light"] .detail-header { background: #dbeafe; }
@@ -308,6 +311,7 @@ body { font: 14px/1.5 system-ui, sans-serif; background: var(--bg); color: var(-
<div class="trace-main"> <div class="trace-main">
<div class="trace-left"> <div class="trace-left">
<div class="apdu-list" id="apdu-list"></div> <div class="apdu-list" id="apdu-list"></div>
<div class="splitter" id="splitter"></div>
<div class="apdu-detail" id="apdu-detail"> <div class="apdu-detail" id="apdu-detail">
<div class="detail-empty" data-l10n="Click an APDU to see details">Click an APDU to see details</div> <div class="detail-empty" data-l10n="Click an APDU to see details">Click an APDU to see details</div>
</div> </div>
@@ -2613,7 +2617,7 @@ function onFilterChange() {
} }
// ──────────────────── Versioning ──────────────────── // ──────────────────── Versioning ────────────────────
const PWA_VERSION = '1.22.11'; const PWA_VERSION = '1.22.12';
const MIN_SERVER_VERSION = '1.0.0'; const MIN_SERVER_VERSION = '1.0.0';
function parseVersion(v) { function parseVersion(v) {
@@ -2968,6 +2972,39 @@ function init() {
updateCaptureButton(); updateCaptureButton();
} }
// ──────────────────── Splitter ────────────────────
(function initSplitter() {
const splitter = el('splitter');
const detail = el('apdu-detail');
const left = splitter.parentElement;
const saved = parseFloat(localStorage.getItem('splitter-ratio'));
if (saved > 0.1 && saved < 0.8) {
left.style.setProperty('--detail-height', (saved * 100) + '%');
}
let startY, startH;
function onMove(e) {
const dy = startY - e.clientY;
const total = left.clientHeight;
const h = Math.min(Math.max(startH + dy, total * 0.1), total * 0.8);
const ratio = h / total;
left.style.setProperty('--detail-height', (ratio * 100) + '%');
localStorage.setItem('splitter-ratio', ratio);
}
function onUp() {
splitter.classList.remove('active');
document.removeEventListener('mousemove', onMove);
document.removeEventListener('mouseup', onUp);
}
splitter.addEventListener('mousedown', e => {
e.preventDefault();
startY = e.clientY;
startH = detail.offsetHeight;
splitter.classList.add('active');
document.addEventListener('mousemove', onMove);
document.addEventListener('mouseup', onUp);
});
})();
init(); init();
</script> </script>
</body> </body>
+1 -1
View File
@@ -1,4 +1,4 @@
const CACHE = 'simtrace-analyser-v43'; const CACHE = 'simtrace-analyser-v44';
const PRECACHE = [ const PRECACHE = [
'index.html', 'index.html',
+1 -1
View File
@@ -4,7 +4,7 @@ build-backend = "setuptools.backends._legacy:_Backend"
[project] [project]
name = "simtrace2-pysniff" name = "simtrace2-pysniff"
version = "1.22.11" version = "1.22.12"
description = "Python-based sniffing tool for SIMtrace2 hardware (SIM card communication sniffer)" description = "Python-based sniffing tool for SIMtrace2 hardware (SIM card communication sniffer)"
readme = {file = "README.md", content-type = "text/markdown"} readme = {file = "README.md", content-type = "text/markdown"}
license = {text = "GPL-2.0-or-later"} license = {text = "GPL-2.0-or-later"}
+1 -1
View File
@@ -6,7 +6,7 @@ from .gsmtap import GsmtapSender, GsmtapReceiver
from .pcap import PcapWriter from .pcap import PcapWriter
from .dump import FileDumper, format_message from .dump import FileDumper, format_message
__version__ = '1.22.11' __version__ = '1.22.12'
__all__ = [ __all__ = [
'SniffSession', 'SniffSession',
'DeviceDisconnected', 'DeviceDisconnected',
+1 -1
View File
@@ -224,7 +224,7 @@ class TestImportEndpoint(unittest.TestCase):
class TestVersion(unittest.TestCase): class TestVersion(unittest.TestCase):
def test_package_version(self): def test_package_version(self):
from simtrace2_pysniff import __version__ from simtrace2_pysniff import __version__
self.assertEqual(__version__, '1.22.11') self.assertEqual(__version__, '1.22.12')
def test_status_includes_version(self): def test_status_includes_version(self):
import os import os