diff --git a/frontend/index.html b/frontend/index.html
index aadaadc..c7d0055 100644
--- a/frontend/index.html
+++ b/frontend/index.html
@@ -1910,7 +1910,7 @@
// ===== Version =====
// Single source of truth for the PWA version: shown in the header and used
// by the server version check in pysimConnect().
-const SIMPLE_VERSION = '3.28.7';
+const SIMPLE_VERSION = '3.28.8';
document.getElementById('app-version').textContent = 'v' + SIMPLE_VERSION;
// ===== view history (the browser Back/Forward buttons) =====
@@ -10267,7 +10267,13 @@ function ramBatchRender() {
counts.textContent = text;
}
const box = document.getElementById('ram-batch-attempts');
- if (box) box.innerHTML = ramBatchAttemptsHtml(st.attempts || []);
+ if (box) {
+ // Replacing the box's content resets its scroll: the render now runs on
+ // every 2 s poll, so keep the reader's position (v3.28.8).
+ const prevScroll = box.scrollTop;
+ box.innerHTML = ramBatchAttemptsHtml(st.attempts || []);
+ if (prevScroll) box.scrollTop = prevScroll;
+ }
}
function ramBatchAttemptsHtml(attempts) {
diff --git a/frontend/sw.js b/frontend/sw.js
index 7d2b5b6..2c5d0d5 100644
--- a/frontend/sw.js
+++ b/frontend/sw.js
@@ -1,4 +1,4 @@
-const CACHE = 'simple-v445';
+const CACHE = 'simple-v446';
const URLS = [
'index.html',
'help.html',
diff --git a/frontend/tests/ram_batch.test.js b/frontend/tests/ram_batch.test.js
index 8384de8..9532a3d 100644
--- a/frontend/tests/ram_batch.test.js
+++ b/frontend/tests/ram_batch.test.js
@@ -45,6 +45,13 @@ function setEl(id, value) {
contains(c) { return !!els[id].classes[c]; },
},
};
+ if (id === 'ram-batch-attempts') {
+ // like a browser: replacing the content resets the scroll position
+ Object.defineProperty(els[id], 'innerHTML', {
+ get() { return this._html || ''; },
+ set(v) { this._html = v; this.scrollTop = 0; },
+ });
+ }
return els[id];
}
function clearEls() {
@@ -302,6 +309,27 @@ test('a hung detailed fetch does not freeze the counts (v3.28.7)', async () => {
app.hangs = false;
});
+test('the attempts table keeps its scroll position across a poll render (v3.28.8)', () => {
+ clearEls();
+ setEl('ram-op', 'batch');
+ setEl('ram-batch-panel', '');
+ setEl('ram-batch-start', '');
+ setEl('ram-batch-stop', '');
+ setEl('ram-batch-clear', '');
+ setEl('ram-batch-spin', '');
+ setEl('ram-batch-state', '');
+ setEl('ram-batch-notice', '');
+ setEl('ram-batch-counts', '');
+ setEl('ram-batch-attempts', '');
+ app.setLast({ active: true, phase: 'waiting_card', attempts: [
+ { ts: 1, iccid: '8970119000004600098', preset: 'p', result: 'installed', duration_ms: 1 }],
+ counts: { installed: 1, failed: 0, skipped: 0 }, attempts_total: 1 });
+ app.ramBatchRender();
+ els['ram-batch-attempts'].scrollTop = 120; // the reader scrolled back
+ app.ramBatchRender(); // the next 2 s poll render
+ assert.strictEqual(els['ram-batch-attempts'].scrollTop, 120);
+});
+
test('a stale fetch guard is released so the report recovers (v3.28.7)', async () => {
clearEls();
setEl('ram-op', 'batch');
diff --git a/pyproject.toml b/pyproject.toml
index a8caa52..4565e59 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project]
name = "pysim-simple-server"
-version = "3.28.7"
+version = "3.28.8"
description = "HTTP REST server wrapping pysim for the SIMple PWA"
requires-python = ">=3.8"
# pysim is a git-only dependency installed explicitly by setup.bat/setup.sh.
diff --git a/pysim_simple_server/server.py b/pysim_simple_server/server.py
index ee63dd9..12a9dec 100644
--- a/pysim_simple_server/server.py
+++ b/pysim_simple_server/server.py
@@ -37,7 +37,7 @@ from osmocom.tlv import BER_TLV_IE
from osmocom.utils import rpad
-VERSION = '3.28.7'
+VERSION = '3.28.8'
MAX_ENVELOPE_SEGMENTS = 5 # max SMS segments for outgoing C-APDU in ENVELOPE