Move capture button left of search; always visible; disabled when server-disabled

- capture-btn moved to first child of trace-topbar (before search input)
- Button always visible: shows Start/Stop/Connect-to-server as before
- When server capture is disabled: grayed-out Start Capture with
  tooltip via existing i18n key 'Capture is disabled ...'
- No more hiding button when viewing saved session or when disabled
- Bump 1.22.4, SW cache v35->v36
This commit is contained in:
catarrh committed 2026-09-06 10:45:11 +03:00
1 parent 0fac73833d
commit 0ec7db8d34
5 files changed
+21 -17

No files matched your search

+17 -13
View File
@@ -114,6 +114,7 @@ body { font: 14px/1.5 system-ui, sans-serif; background: var(--bg); color: var(-
.trace-topbar button.stop:hover { background: var(--danger-hover); } .trace-topbar button.stop:hover { background: var(--danger-hover); }
.trace-topbar button.connect { background: var(--warn); color: #0f172a; } .trace-topbar button.connect { background: var(--warn); color: #0f172a; }
.trace-topbar button.connect:hover { background: var(--warn-hover); } .trace-topbar button.connect:hover { background: var(--warn-hover); }
.trace-topbar button.disabled { background: var(--bg-input); color: var(--text-muted); cursor: not-allowed; opacity: 0.6; }
.trace-main { display: flex; flex: 1; overflow: hidden; } .trace-main { display: flex; flex: 1; overflow: hidden; }
.trace-left { flex: 1; display: flex; flex-direction: column; overflow: hidden; } .trace-left { flex: 1; display: flex; flex-direction: column; overflow: hidden; }
@@ -280,15 +281,15 @@ body { font: 14px/1.5 system-ui, sans-serif; background: var(--bg); color: var(-
</div> </div>
</div><!-- Trace Panel --> </div><!-- Trace Panel -->
<div class="panel active" id="panel-trace"> <div class="panel active" id="panel-trace">
<div class="trace-topbar"> <div class="trace-topbar">
<button id="capture-btn" data-l10n="Start Capture">Start Capture</button>
<input type="text" id="search-input" data-l10n="Search hex bytes..." placeholder="Search hex bytes..." autocomplete="off"> <input type="text" id="search-input" data-l10n="Search hex bytes..." placeholder="Search hex bytes..." autocomplete="off">
<label for="filter-select" class="filter-label" data-l10n="Filter">Filter</label> <label for="filter-select" class="filter-label" data-l10n="Filter">Filter</label>
<select id="filter-select"> <select id="filter-select">
<option value="" data-l10n="All commands">All commands</option> <option value="" data-l10n="All commands">All commands</option>
</select> </select>
<button id="capture-btn" data-l10n="Start Capture">Start Capture</button>
<span id="session-name-display" style="display:none; color:var(--text-muted); font-size:15px; font-weight:700;"></span> <span id="session-name-display" style="display:none; color:var(--text-muted); font-size:15px; font-weight:700;"></span>
</div> </div>
<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>
@@ -2130,26 +2131,29 @@ async function stopCapture() {
function updateCaptureButton() { function updateCaptureButton() {
const btn = el('capture-btn'); const btn = el('capture-btn');
const newCapture = el('new-capture-btn'); const newCapture = el('new-capture-btn');
btn.classList.remove('stop', 'connect'); btn.classList.remove('stop', 'connect', 'disabled');
const captureOff = STATE.captureMode === 'disabled' && STATE.serverAvailable; const captureOff = STATE.captureMode === 'disabled' && STATE.serverAvailable;
newCapture.style.display = captureOff ? 'none' : ''; newCapture.style.display = captureOff ? 'none' : '';
if (captureOff) {
btn.style.display = 'none';
return;
}
if (STATE.sessionViewId && !STATE.captureActive) {
btn.style.display = 'none';
return;
}
btn.style.display = ''; btn.style.display = '';
if (!STATE.serverAvailable) { if (!STATE.serverAvailable) {
btn.textContent = t('Connect to server'); btn.textContent = t('Connect to server');
btn.classList.add('connect'); btn.classList.add('connect');
btn.disabled = false;
btn.title = '';
} else if (captureOff) {
btn.textContent = t('Start Capture');
btn.classList.add('disabled');
btn.disabled = true;
btn.title = t('Capture is disabled — only pre-captured traces (saved sessions / PCAP import) are available.');
} else if (STATE.captureActive) { } else if (STATE.captureActive) {
btn.textContent = t('Stop Capture'); btn.textContent = t('Stop Capture');
btn.classList.add('stop'); btn.classList.add('stop');
btn.disabled = false;
btn.title = '';
} else { } else {
btn.textContent = t('Start Capture'); btn.textContent = t('Start Capture');
btn.disabled = false;
btn.title = '';
} }
} }
@@ -2518,7 +2522,7 @@ function onFilterChange() {
} }
// ──────────────────── Versioning ──────────────────── // ──────────────────── Versioning ────────────────────
const PWA_VERSION = '1.22.3'; const PWA_VERSION = '1.22.4';
const MIN_SERVER_VERSION = '1.0.0'; const MIN_SERVER_VERSION = '1.0.0';
function parseVersion(v) { function parseVersion(v) {
+1 -1
View File
@@ -1,4 +1,4 @@
const CACHE = 'simtrace-analyser-v35'; const CACHE = 'simtrace-analyser-v36';
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.3" version = "1.22.4"
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.3' __version__ = '1.22.4'
__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.3') self.assertEqual(__version__, '1.22.4')
def test_status_includes_version(self): def test_status_includes_version(self):
import os import os