v3.23.3: the low-counter notice polish

The two buttons under a cntr_low verdict shared the red warning style, and
the notice only ever rendered in the RAM pill: an SCP80 send wrote it into
the hidden RAM container, so the plain-send view showed the verdict text
but offered no way to act on it.

- The counter probe button is amber and the preset jump green (the warning
  text stays red).
- The probe renders the inline throbber the RAM operations use
  (animate-spin, no modal) while it runs; its result replaces it in the
  same box.
- Each pill carries its own notice box (#ram-cntr-low / #sp-cntr-low):
  ramShowCntrLow/ramSyncCounter target the originating view's box and
  clear the other, the SCP80 send passes 'sp' explicitly (it used to rely
  on the keyset-view heuristic), and a new send attempt clears the stale
  verdict (ramClearResults clears both boxes).
- The prebuilt Tailwind output was regenerated (npm run build):
  border-amber-300/-700 and border-emerald-300/-700 were absent from
  style.css (the stale-style trap).

Tests: the button classes, the spinner and the box targeting pinned in
ram.test.js.  846 frontend + 830 Python; sw cache simple-v416.
This commit is contained in:
2026-10-07 10:28:42 +03:00
parent 9ad22f2e26
commit 17863001f3
7 changed files with 97 additions and 24 deletions
+43 -18
View File
@@ -684,6 +684,7 @@
<textarea id="sp-result" rows="3" readonly class="w-full font-mono border border-gray-300 dark:border-slate-600 text-sm rounded px-3 py-1.5 bg-gray-100 dark:bg-slate-800"></textarea> <textarea id="sp-result" rows="3" readonly class="w-full font-mono border border-gray-300 dark:border-slate-600 text-sm rounded px-3 py-1.5 bg-gray-100 dark:bg-slate-800"></textarea>
<div id="sp-size-info" class="text-xs mt-1 text-gray-500 dark:text-slate-400"></div> <div id="sp-size-info" class="text-xs mt-1 text-gray-500 dark:text-slate-400"></div>
<div id="sp-send-result" class="mt-2 text-xs font-mono hidden"></div> <div id="sp-send-result" class="mt-2 text-xs font-mono hidden"></div>
<div id="sp-cntr-low" class="mt-2 hidden"></div>
<div id="sp-verify-result" class="mt-2 text-xs font-mono hidden"></div> <div id="sp-verify-result" class="mt-2 text-xs font-mono hidden"></div>
</div> </div>
@@ -1904,7 +1905,7 @@
// ===== Version ===== // ===== Version =====
// Single source of truth for the PWA version: shown in the header and used // Single source of truth for the PWA version: shown in the header and used
// by the server version check in pysimConnect(). // by the server version check in pysimConnect().
const SIMPLE_VERSION = '3.23.2'; const SIMPLE_VERSION = '3.23.3';
document.getElementById('app-version').textContent = 'v' + SIMPLE_VERSION; document.getElementById('app-version').textContent = 'v' + SIMPLE_VERSION;
// ===== view history (the browser Back/Forward buttons) ===== // ===== view history (the browser Back/Forward buttons) =====
@@ -9639,6 +9640,9 @@ async function pysimSendOta() {
sendResultEl.classList.add('text-gray-500'); sendResultEl.classList.add('text-gray-500');
sendResultEl.textContent = t('Sending OTA...'); sendResultEl.textContent = t('Sending OTA...');
porStatusEl.classList.add('hidden'); porStatusEl.classList.add('hidden');
// a new attempt invalidates the previous low-counter verdict (the notice
// stays in the pill until then; v3.23.3)
ramCntrLowClear();
try { try {
const data = await pysimFetch('/api/send-ota', { sp, ...spParams() }); const data = await pysimFetch('/api/send-ota', { sp, ...spParams() });
// low counter: not recoverable by retrying (v3.6.58) // low counter: not recoverable by retrying (v3.6.58)
@@ -9646,7 +9650,7 @@ async function pysimSendOta() {
sendResultEl.textContent = t('Low counter') + ' — ' + t('the card rejected the packet'); sendResultEl.textContent = t('Low counter') + ' — ' + t('the card rejected the packet');
sendResultEl.classList.remove('text-gray-500', 'text-green-600'); sendResultEl.classList.remove('text-gray-500', 'text-green-600');
sendResultEl.classList.add('text-red-600'); sendResultEl.classList.add('text-red-600');
ramShowCntrLow(); ramShowCntrLow(undefined, 'sp');
return; return;
} }
if (data.success) { if (data.success) {
@@ -9898,8 +9902,7 @@ function ramClearResults() {
document.getElementById('ram-explorer').innerHTML = ''; document.getElementById('ram-explorer').innerHTML = '';
document.getElementById('ram-steps').classList.add('hidden'); document.getElementById('ram-steps').classList.add('hidden');
document.getElementById('ram-steps').textContent = ''; document.getElementById('ram-steps').textContent = '';
const cntrLowEl = document.getElementById('ram-cntr-low'); ramCntrLowClear();
if (cntrLowEl) { cntrLowEl.innerHTML = ''; cntrLowEl.classList.add('hidden'); }
} }
// Read a File as a hex string via FileReader (client-side, no upload) // Read a File as a hex string via FileReader (client-side, no upload)
@@ -11576,7 +11579,7 @@ async function ramExplore(sp) {
// the run was stopped: state the verdict, do not retry // the run was stopped: state the verdict, do not retry
resultEl.textContent = t('Low counter') + ' — ' + t('the operation was stopped'); resultEl.textContent = t('Low counter') + ' — ' + t('the operation was stopped');
resultEl.classList.remove('hidden', 'text-green-600'); resultEl.classList.add('text-red-600'); resultEl.classList.remove('hidden', 'text-green-600'); resultEl.classList.add('text-red-600');
ramShowCntrLow(); ramShowCntrLow(undefined, 'ram');
} else if (errors.length) { } else if (errors.length) {
resultEl.textContent = t('Partial') + ' — ' + errors.join('; '); resultEl.textContent = t('Partial') + ' — ' + errors.join('; ');
resultEl.classList.remove('hidden', 'text-green-600'); resultEl.classList.add('text-red-600'); resultEl.classList.remove('hidden', 'text-green-600'); resultEl.classList.add('text-red-600');
@@ -11685,7 +11688,7 @@ async function ramDeleteFromExplorer(aid, withCascade) {
const el = document.getElementById('ram-result'); const el = document.getElementById('ram-result');
el.textContent = t('Low counter') + ' — ' + t('the operation was stopped'); el.textContent = t('Low counter') + ' — ' + t('the operation was stopped');
el.classList.remove('hidden', 'text-green-600'); el.classList.add('text-red-600'); el.classList.remove('hidden', 'text-green-600'); el.classList.add('text-red-600');
ramShowCntrLow(); ramShowCntrLow(undefined, 'ram');
return; return;
} }
// The card consumes the counter as soon as it accepts the packet, even // The card consumes the counter as soon as it accepts the packet, even
@@ -12119,7 +12122,7 @@ async function ramCompatTest(sp) {
if (spCntrLow(data)) { if (spCntrLow(data)) {
const v = ramCntrLowView(); const v = ramCntrLowView();
modal += ramCntrLowHtml(ramCntrLowPresetIdx(v), v); modal += ramCntrLowHtml(ramCntrLowPresetIdx(v), v);
ramShowCntrLow(undefined, v); ramShowCntrLow(undefined, 'ram');
} }
ramOpModalFinish(verdict, !!data.imports_ok, modal, true); ramOpModalFinish(verdict, !!data.imports_ok, modal, true);
} }
@@ -12510,23 +12513,44 @@ function ramCntrLowHtml(presetIdx, view) {
const idx = Number.isInteger(presetIdx) && presetIdx >= 0 ? presetIdx : -1; const idx = Number.isInteger(presetIdx) && presetIdx >= 0 ? presetIdx : -1;
const v = view === 'sp' ? 'sp' : 'ram'; const v = view === 'sp' ? 'sp' : 'ram';
const text = t('Low counter') + ' \u2014 ' + t('the card rejected the packet'); const text = t('Low counter') + ' \u2014 ' + t('the card rejected the packet');
const btnCls = 'px-2 py-1 text-xs rounded border border-red-300 dark:border-red-700 ' + // the probe is amber (a corrective action), the preset jump green (the
'hover:bg-red-100 dark:hover:bg-red-900/40 whitespace-nowrap'; // manual fix) - v3.23.3, they used to share the red warning style
const base = 'px-2 py-1 text-xs rounded border whitespace-nowrap ';
const syncCls = base + 'border-amber-300 dark:border-amber-700 text-amber-700 ' +
'dark:text-amber-300 hover:bg-amber-100 dark:hover:bg-amber-900/40';
const goCls = base + 'border-emerald-300 dark:border-emerald-700 text-emerald-700 ' +
'dark:text-emerald-300 hover:bg-emerald-100 dark:hover:bg-emerald-900/40';
return '<div class="flex items-center gap-3 text-sm text-red-600 dark:text-red-400">' + return '<div class="flex items-center gap-3 text-sm text-red-600 dark:text-red-400">' +
'<span>' + text + '</span>' + '<span>' + text + '</span>' +
'<button type="button" onclick="ramSyncCounter(' + idx + ', \'' + v + '\')" class="' + btnCls + '">' + '<button type="button" onclick="ramSyncCounter(' + idx + ', \'' + v + '\')" class="' + syncCls + '">' +
esc(t('Sync counter')) + '</button>' + esc(t('Sync counter')) + '</button>' +
'<button type="button" onclick="ramGoToPreset(' + idx + ')" class="' + btnCls + '">' + '<button type="button" onclick="ramGoToPreset(' + idx + ')" class="' + goCls + '">' +
esc(t('Go to preset')) + '</button></div>'; esc(t('Go to preset')) + '</button></div>';
} }
// Show the low-counter notice in the RAM result area (and, when the operation // The low-counter notice box of a view: the RAM and Secured packet pills
// modal is still open, in its detail block). // each carry one, so the notice (and the counter probe's progress) is visible
// in the view the failed operation ran in (v3.23.3).
function ramCntrLowBox(view) {
return document.getElementById(view === 'sp' ? 'sp-cntr-low' : 'ram-cntr-low');
}
function ramCntrLowClear() {
['ram-cntr-low', 'sp-cntr-low'].forEach(id => {
const el = document.getElementById(id);
if (el) { el.innerHTML = ''; el.classList.add('hidden'); }
});
}
// Show the low-counter notice in the originating view's box (the other one is
// cleared: only the latest verdict stays on screen) and, when the operation
// modal is still open, in its detail block.
function ramShowCntrLow(presetIdx, view) { function ramShowCntrLow(presetIdx, view) {
const v = view === undefined ? ramCntrLowView() : view; const v = view === undefined ? ramCntrLowView() : view;
const idx = presetIdx === undefined ? ramCntrLowPresetIdx(v) : presetIdx; const idx = presetIdx === undefined ? ramCntrLowPresetIdx(v) : presetIdx;
const html = ramCntrLowHtml(idx, v); const html = ramCntrLowHtml(idx, v);
const el = document.getElementById('ram-cntr-low'); ramCntrLowClear();
const el = ramCntrLowBox(v);
if (el && el.classList) { if (el && el.classList) {
el.innerHTML = html; el.innerHTML = html;
el.classList.remove('hidden'); el.classList.remove('hidden');
@@ -12545,7 +12569,7 @@ function ramShowCntrLow(presetIdx, view) {
async function ramSyncCounter(presetIdx, view) { async function ramSyncCounter(presetIdx, view) {
const i = Number(presetIdx); const i = Number(presetIdx);
const preset = (Number.isInteger(i) && i >= 0 && cards[i]) ? cards[i] : null; const preset = (Number.isInteger(i) && i >= 0 && cards[i]) ? cards[i] : null;
const box = document.getElementById('ram-cntr-low'); const box = ramCntrLowBox(view === 'sp' ? 'sp' : 'ram');
if (!preset) { alert(t('No card preset selected')); return; } if (!preset) { alert(t('No card preset selected')); return; }
// The keyset the failed operation used: the originating view's selector // The keyset the failed operation used: the originating view's selector
// first, the other view's as the fallback (both hold the keyset number as // first, the other view's as the fallback (both hold the keyset number as
@@ -12572,8 +12596,9 @@ async function ramSyncCounter(presetIdx, view) {
else if (val('sp-spi2-hex')) body.spi2 = val('sp-spi2-hex'); else if (val('sp-spi2-hex')) body.spi2 = val('sp-spi2-hex');
const cntr = val('sp-cntr').replace(/[^0-9a-fA-F]/g, ''); const cntr = val('sp-cntr').replace(/[^0-9a-fA-F]/g, '');
if (cntr) body.cntr = cntr; if (cntr) body.cntr = cntr;
if (box) box.innerHTML = '<div class="text-sm text-gray-500 dark:text-slate-400">' + if (box) box.innerHTML = '<div class="flex items-center gap-2 text-sm text-gray-500 dark:text-slate-400">' +
esc(t('Syncing counter...')) + '</div>'; '<div class="animate-spin w-4 h-4 border-2 border-blue-500 border-t-transparent rounded-full"></div>' +
'<span>' + esc(t('Syncing counter...')) + '</span></div>';
try { try {
const data = await pysimFetch('/api/counter-probe', body); const data = await pysimFetch('/api/counter-probe', body);
if (data.success || data.counter_saved) { if (data.success || data.counter_saved) {
@@ -12677,7 +12702,7 @@ function ramShowInstallResult(data, okText, capMem) {
if (spCntrLow(data)) { if (spCntrLow(data)) {
const v = ramCntrLowView(); const v = ramCntrLowView();
modal += ramCntrLowHtml(ramCntrLowPresetIdx(v), v); modal += ramCntrLowHtml(ramCntrLowPresetIdx(v), v);
ramShowCntrLow(undefined, v); ramShowCntrLow(undefined, 'ram');
} }
ramOpModalFinish(resultEl.textContent, !!data.success, modal, true); ramOpModalFinish(resultEl.textContent, !!data.success, modal, true);
} }
+35 -2
View File
@@ -1307,6 +1307,11 @@ video {
border-top-width: 1px; border-top-width: 1px;
} }
.border-amber-300 {
--tw-border-opacity: 1;
border-color: rgb(252 211 77 / var(--tw-border-opacity, 1));
}
.border-amber-400 { .border-amber-400 {
--tw-border-opacity: 1; --tw-border-opacity: 1;
border-color: rgb(251 191 36 / var(--tw-border-opacity, 1)); border-color: rgb(251 191 36 / var(--tw-border-opacity, 1));
@@ -1327,6 +1332,11 @@ video {
border-color: rgb(59 130 246 / var(--tw-border-opacity, 1)); border-color: rgb(59 130 246 / var(--tw-border-opacity, 1));
} }
.border-emerald-300 {
--tw-border-opacity: 1;
border-color: rgb(110 231 183 / var(--tw-border-opacity, 1));
}
.border-gray-100 { .border-gray-100 {
--tw-border-opacity: 1; --tw-border-opacity: 1;
border-color: rgb(243 244 246 / var(--tw-border-opacity, 1)); border-color: rgb(243 244 246 / var(--tw-border-opacity, 1));
@@ -1887,6 +1897,11 @@ video {
color: #f1f5f9; color: #f1f5f9;
} }
.hover\:bg-amber-100:hover {
--tw-bg-opacity: 1;
background-color: rgb(254 243 199 / var(--tw-bg-opacity, 1));
}
.hover\:bg-amber-700:hover { .hover\:bg-amber-700:hover {
--tw-bg-opacity: 1; --tw-bg-opacity: 1;
background-color: rgb(180 83 9 / var(--tw-bg-opacity, 1)); background-color: rgb(180 83 9 / var(--tw-bg-opacity, 1));
@@ -1897,6 +1912,11 @@ video {
background-color: rgb(29 78 216 / var(--tw-bg-opacity, 1)); background-color: rgb(29 78 216 / var(--tw-bg-opacity, 1));
} }
.hover\:bg-emerald-100:hover {
--tw-bg-opacity: 1;
background-color: rgb(209 250 229 / var(--tw-bg-opacity, 1));
}
.hover\:bg-emerald-200:hover { .hover\:bg-emerald-200:hover {
--tw-bg-opacity: 1; --tw-bg-opacity: 1;
background-color: rgb(167 243 208 / var(--tw-bg-opacity, 1)); background-color: rgb(167 243 208 / var(--tw-bg-opacity, 1));
@@ -2017,14 +2037,19 @@ video {
border-color: rgb(217 119 6 / var(--tw-border-opacity, 1)); border-color: rgb(217 119 6 / var(--tw-border-opacity, 1));
} }
.dark\:border-amber-700:is(.dark *) {
--tw-border-opacity: 1;
border-color: rgb(180 83 9 / var(--tw-border-opacity, 1));
}
.dark\:border-blue-800:is(.dark *) { .dark\:border-blue-800:is(.dark *) {
--tw-border-opacity: 1; --tw-border-opacity: 1;
border-color: rgb(30 64 175 / var(--tw-border-opacity, 1)); border-color: rgb(30 64 175 / var(--tw-border-opacity, 1));
} }
.dark\:border-red-700:is(.dark *) { .dark\:border-emerald-700:is(.dark *) {
--tw-border-opacity: 1; --tw-border-opacity: 1;
border-color: rgb(185 28 28 / var(--tw-border-opacity, 1)); border-color: rgb(4 120 87 / var(--tw-border-opacity, 1));
} }
.dark\:border-red-800:is(.dark *) { .dark\:border-red-800:is(.dark *) {
@@ -2225,6 +2250,14 @@ video {
filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow); filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow);
} }
.dark\:hover\:bg-amber-900\/40:hover:is(.dark *) {
background-color: rgb(120 53 15 / 0.4);
}
.dark\:hover\:bg-emerald-900\/40:hover:is(.dark *) {
background-color: rgb(6 78 59 / 0.4);
}
.dark\:hover\:bg-red-900\/40:hover:is(.dark *) { .dark\:hover\:bg-red-900\/40:hover:is(.dark *) {
background-color: rgb(127 29 29 / 0.4); background-color: rgb(127 29 29 / 0.4);
} }
+1 -1
View File
@@ -1,4 +1,4 @@
const CACHE = 'simple-v415'; const CACHE = 'simple-v416';
const URLS = [ const URLS = [
'index.html', 'index.html',
'help.html', 'help.html',
+15 -1
View File
@@ -28,6 +28,7 @@ const FNS = ['berLenStr', 'buildApdu', 'escHtml', 'esc', 'chainInit', 'chainRamB
'_parseRawElfEntry', '_parseRawAppEntry', 'ramParseElfStatus', 'ramParseAppStatus', 'ramParseModuleAids', 'parseTLV', '_parseE3Entry', '_parseMenuEntries', '_parseRawElfEntry', '_parseRawAppEntry', 'ramParseElfStatus', 'ramParseAppStatus', 'ramParseModuleAids', 'parseTLV', '_parseE3Entry', '_parseMenuEntries',
'ramExpandedReport', 'ramExpandedMore', 'ramPageFormats', 'ramAppsMissingDetails', 'ramExpandedTags', 'ramExpandedGroups', 'ramExpandedElfForm', 'ramElfVersionHint', 'cardEtsiRelease', 'ramChainGetResponse', 'ramDeriveElfVersions', 'ramElfAppletCandidate', 'ramElfAppMatch', 'ramExpandedReport', 'ramExpandedMore', 'ramPageFormats', 'ramAppsMissingDetails', 'ramExpandedTags', 'ramExpandedGroups', 'ramExpandedElfForm', 'ramElfVersionHint', 'cardEtsiRelease', 'ramChainGetResponse', 'ramDeriveElfVersions', 'ramElfAppletCandidate', 'ramElfAppMatch',
'spCntrLow', 'ramCntrLowHtml', 'ramCntrLowPresetIdx', 'ramCntrLowView', 'ramShowCntrLow', 'spCntrLow', 'ramCntrLowHtml', 'ramCntrLowPresetIdx', 'ramCntrLowView', 'ramShowCntrLow',
'ramCntrLowBox', 'ramCntrLowClear',
'spKeysetKvnOf', 'spKeysetList', 'spKeysetFor', 'spKeysetCheck', 'spKeysetKvnOf', 'spKeysetList', 'spKeysetFor', 'spKeysetCheck',
'spKeysetOptionsHtml', 'spPresetIdx', 'tarPresetIdx', 'spKeysetOptionsHtml', 'spPresetIdx', 'tarPresetIdx',
'cardsKeysetsFromForm', 'spKeysetSync', 'spKeysetApply', 'spKeysetChanged', 'cardsKeysetsFromForm', 'spKeysetSync', 'spKeysetApply', 'spKeysetChanged',
@@ -913,6 +914,18 @@ test('ramCntrLowHtml states the facts without a fabricated counter', () => {
assert.ok(noPreset.includes('onclick="ramGoToPreset(-1)"'), noPreset); assert.ok(noPreset.includes('onclick="ramGoToPreset(-1)"'), noPreset);
assert.ok(noPreset.includes('onclick="ramSyncCounter(-1, \'ram\')"'), noPreset); assert.ok(noPreset.includes('onclick="ramSyncCounter(-1, \'ram\')"'), noPreset);
assert.ok(spView.includes('onclick="ramSyncCounter(1, \'sp\')"'), spView); assert.ok(spView.includes('onclick="ramSyncCounter(1, \'sp\')"'), spView);
// v3.23.3: the probe button is amber, the preset jump green
const syncBtn = out.match(/<button[^>]*ramSyncCounter[^>]*>/)[0];
assert.ok(syncBtn.includes('border-amber-300'), syncBtn);
assert.ok(!syncBtn.includes('border-red-300'), syncBtn);
const goBtn = out.match(/<button[^>]*ramGoToPreset[^>]*>/)[0];
assert.ok(goBtn.includes('border-emerald-300'), goBtn);
// the notice targets the originating view's box; the probe renders the
// inline throbber (the RAM inline-progress spinner, no modal)
assert.match(extractFunc(html, 'ramShowCntrLow'), /ramCntrLowBox\(v\)/);
assert.match(extractFunc(html, 'ramSyncCounter'), /animate-spin/);
assert.match(extractFunc(html, 'ramSyncCounter'), /ramCntrLowBox\(/);
assert.match(html, /id="sp-cntr-low"/);
}); });
test('the explorer shows versions on app and ISD rows too', () => { test('the explorer shows versions on app and ISD rows too', () => {
@@ -963,7 +976,8 @@ test('every SCP80/RAM flow checks the low counter and stops', () => {
const explore = extractFunc(html, 'ramExplore'); const explore = extractFunc(html, 'ramExplore');
assert.ok(explore.includes('spCntrLow(res)'), 'the Explore must check every send'); assert.ok(explore.includes('spCntrLow(res)'), 'the Explore must check every send');
assert.ok(explore.includes('if (cntrLow) break;'), 'the loops must stop'); assert.ok(explore.includes('if (cntrLow) break;'), 'the loops must stop');
assert.ok(explore.includes('ramShowCntrLow()'), 'the verdict must be shown'); assert.ok(explore.includes("ramShowCntrLow(undefined, 'ram')"),
'the verdict must be shown in the RAM view');
// the delete flow and the plain send check it too // the delete flow and the plain send check it too
assert.ok(extractFunc(html, 'ramDeleteFromExplorer').includes('spCntrLow(res)'), assert.ok(extractFunc(html, 'ramDeleteFromExplorer').includes('spCntrLow(res)'),
'the delete flow must stop'); 'the delete flow must stop');
+1
View File
@@ -32,6 +32,7 @@ for (const fn of ['berLenStr', 'ramDeleteApdu', 'ramIncrementCntr', 'ramSaveCntr
'spPresetIdx', 'spPresetCounterValue', 'spCntrPlaceholder', 'spCntrAhead', 'spPresetIdx', 'spPresetCounterValue', 'spCntrPlaceholder', 'spCntrAhead',
'ramHideProgress', 'ramDeleteFromExplorer', 'ramHideProgress', 'ramDeleteFromExplorer',
'spCntrLow', 'ramCntrLowHtml', 'ramCntrLowPresetIdx', 'ramCntrLowView', 'ramShowCntrLow', 'spCntrLow', 'ramCntrLowHtml', 'ramCntrLowPresetIdx', 'ramCntrLowView', 'ramShowCntrLow',
'ramCntrLowBox', 'ramCntrLowClear',
'escHtml', 'esc', 'escHtml', 'esc',
'spKeysetKvnOf', 'spKeysetList', 'spKeysetFor', 'spKeysetCheck', 'spKeysetKvnOf', 'spKeysetList', 'spKeysetFor', 'spKeysetCheck',
'spKeysetOptionsHtml', 'cardsKeysetRowHtml', 'cardsKeysetRowsRender', 'spKeysetOptionsHtml', 'cardsKeysetRowHtml', 'cardsKeysetRowsRender',
+1 -1
View File
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project] [project]
name = "pysim-simple-server" name = "pysim-simple-server"
version = "3.23.2" version = "3.23.3"
description = "HTTP REST server wrapping pysim for the SIMple PWA" description = "HTTP REST server wrapping pysim for the SIMple PWA"
requires-python = ">=3.8" requires-python = ">=3.8"
# pysim is a git-only dependency installed explicitly by setup.bat/setup.sh. # pysim is a git-only dependency installed explicitly by setup.bat/setup.sh.
+1 -1
View File
@@ -36,7 +36,7 @@ from osmocom.tlv import BER_TLV_IE
from osmocom.utils import rpad from osmocom.utils import rpad
VERSION = '3.23.2' VERSION = '3.23.3'
MAX_ENVELOPE_SEGMENTS = 5 # max SMS segments for outgoing C-APDU in ENVELOPE MAX_ENVELOPE_SEGMENTS = 5 # max SMS segments for outgoing C-APDU in ENVELOPE