feat: decode the Items Next Action Indicator in proactive commands (v3.5.3)

SET UP MENU / SELECT ITEM items carry an optional Items Next Action Indicator
(TS 102 223 8.24, tag '18'): one byte per item, in list order, coded with the
Table 9.4 values marked "Used for Next Action Indicator".  The decoder ignored
it, so neither the proactive log nor the STK menu showed what the card would
do when an item is selected.

- server: NAI_TYPES whitelist (the ToC-only values - e.g. '26', '27', '47' -
  are reserved there and are ignored, as are '00' and unlisted values),
  _nai_name(), _attach_nai() (a short NAI list leaves the tail without an
  indicator, extra bytes are ignored); _parse_select_item() and
  _parse_setup_menu_items() plus the TERMINAL PROFILE SET UP MENU walk attach
  nai/nai_name to the items; _decode_cmd() renders '1. Menu -> SET UP MENU'.
- PWA: stkMenuNaiSuffix() adds a small gray '<name>' suffix to STK menu items
  that carry an NAI; the proactive log picks the decode up via cmd_decoded.
- tests: SET UP MENU / SELECT ITEM vectors with NAIs, a reserved value that is
  ignored, a short NAI list, and the stkMenuNaiSuffix unit test.
- docs: UICC_SPECS 6.5 gained the '18' Items next action indicator row
  (8.24/9.4); help EN/RU mention the menu suffix; AGENTS files updated.

548 frontend / 413 Python green; version 3.5.3; sw cache simple-v255.
This commit is contained in:
2026-09-24 20:37:53 +03:00
parent 0f8c6dea50
commit 337e5df770
8 changed files with 125 additions and 11 deletions
+10 -2
View File
@@ -1559,7 +1559,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.5.2';
const SIMPLE_VERSION = '3.5.3';
document.getElementById('app-version').textContent = 'v' + SIMPLE_VERSION;
// ===== Tab switching =====
@@ -7693,6 +7693,12 @@ async function stkMenuExit() {
stkMenuClose();
}
// TS 102 223 8.24: the item's next action (coded like the Type of Command);
// the server only attaches nai_name for values the table marks for the NAI.
function stkMenuNaiSuffix(item) {
return (item && item.nai_name) ? '\u25b8 ' + item.nai_name : '';
}
function stkMenuRenderItems() {
const content = document.getElementById('stk-menu-panel-content');
content.innerHTML = '<span class="text-gray-500 text-sm">Loading...</span>';
@@ -7700,7 +7706,9 @@ function stkMenuRenderItems() {
if (!data || !data.items) { content.innerHTML = '<span class="text-red-500 text-sm">No menu data</span>'; return; }
let html = '<div class="space-y-1">';
data.items.forEach(item => {
html += '<div class="flex items-center gap-2 px-2 py-1.5 rounded hover:bg-gray-100 dark:hover:bg-slate-700 cursor-pointer" onclick="stkMenuItemClick(' + item.id + ')"><span class="w-5 text-xs text-gray-300 dark:text-gray-600 text-right">' + item.id + '</span><span class="text-sm">' + esc(item.text) + '</span></div>';
const nai = stkMenuNaiSuffix(item);
html += '<div class="flex items-center gap-2 px-2 py-1.5 rounded hover:bg-gray-100 dark:hover:bg-slate-700 cursor-pointer" onclick="stkMenuItemClick(' + item.id + ')"><span class="w-5 text-xs text-gray-300 dark:text-gray-600 text-right">' + item.id + '</span><span class="text-sm">' + esc(item.text) + '</span>' +
(nai ? '<span class="text-xs text-gray-400 dark:text-slate-500">' + esc(nai) + '</span>' : '') + '</div>';
});
html += '</div>';
content.innerHTML = html;