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:
1 parent
7d8dca1ffd
commit
52243f496c
5 files changed
+44
-7
No files matched your search
+40
-3
@@ -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
@@ -1,4 +1,4 @@
|
|||||||
const CACHE = 'simtrace-analyser-v43';
|
const CACHE = 'simtrace-analyser-v44';
|
||||||
|
|
||||||
const PRECACHE = [
|
const PRECACHE = [
|
||||||
'index.html',
|
'index.html',
|
||||||
|
|||||||
+1
-1
@@ -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"}
|
||||||
|
|||||||
@@ -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',
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in new issue
Block a user