ui: give the Requires table its own AID column (v3.5.18)

The imported-library AIDs are now shown in a dedicated column between the
library name and the minimum version (Library | AID | Version | Family |
Refs), monospace and break-all so the 32-char UICC/3GPP AIDs wrap cleanly
on narrow panels.  When a library name cannot be resolved (vendor AID)
the name cell shows a dash instead of repeating the AID now carried by
the column.

Tests updated: the Requires slice asserts the AID header and every row's
AID, and the unresolved-name case expects the dash plus the AID column.
Help EN/RU reworded; the AID key is a no-op in RU.

592 frontend / 473 Python green; version 3.5.18; sw simple-v271.
This commit is contained in:
2026-09-27 13:11:28 +03:00
parent d37a29b389
commit b43e3118cf
7 changed files with 22 additions and 18 deletions
+1 -1
View File
@@ -319,7 +319,7 @@
<thead><tr class="border-b border-gray-300 dark:border-slate-700"><th class="text-left py-1 px-2">Операция</th><th class="text-left py-1 px-2">Описание</th></tr></thead>
<tbody>
<tr class="border-b border-gray-200 dark:border-slate-700"><td class="py-1 px-2">Обзор карты (все данные GP)</td><td class="py-1 px-2">Запрос GET STATUS для ISD, приложений, ELF и модулей ELF, а также GET DATA FF21 для информации о памяти. Результаты отображаются в обзоре с кнопками <strong>Удалить</strong> для каждого элемента.</td></tr>
<tr><td class="py-1 px-2">Установка пакета (.cap файл)</td><td class="py-1 px-2">Отправка <code class="font-mono text-sm">.cap</code> файла на карту через сервер: INSTALL[for load] &rarr; LOAD &times;N &rarr; INSTALL[for install (+make selectable)]. Load-файл делится на LOAD APDU размера <strong>размер блока LOAD</strong> (1&ndash;240 байт полезной нагрузки, по умолчанию 240); каждый защищённый пакет доставляется одним SMS или, если он больше, конкатенированной SMS-PP загрузкой до 5 сегментов, так что большой <code>.cap</code> просто занимает несколько SMS. Сразу после выбора файл проверяется и в форме показывается <strong>оценка требований к памяти</strong>: <strong>требование NVRAM</strong> (образ кода + постоянные данные, сумма в стиле C6+C8 из GP Card Spec) и оценка RAM. Там же показаны <strong>библиотеки, с которыми слинкован CAP</strong> (его компонент Import, в виде <code class="font-mono text-sm">имя ≥ версия</code> — имена стандартных библиотек подставляются, а где известно, даётся подсказка о версии Java Card), идентификаторы пакета и апплетов и разбивка по компонентам (размеры и доли в порядке load-файла) — всё сгруппировано по разделам <strong>Package</strong>, <strong>Requires</strong>, <strong>Memory</strong> и <strong>Components</strong>. Повреждённый файл не проходит анализ, и <strong>Execute</strong> остаётся недоступной до успешного анализа.</td></tr>
<tr><td class="py-1 px-2">Установка пакета (.cap файл)</td><td class="py-1 px-2">Отправка <code class="font-mono text-sm">.cap</code> файла на карту через сервер: INSTALL[for load] &rarr; LOAD &times;N &rarr; INSTALL[for install (+make selectable)]. Load-файл делится на LOAD APDU размера <strong>размер блока LOAD</strong> (1&ndash;240 байт полезной нагрузки, по умолчанию 240); каждый защищённый пакет доставляется одним SMS или, если он больше, конкатенированной SMS-PP загрузкой до 5 сегментов, так что большой <code>.cap</code> просто занимает несколько SMS. Сразу после выбора файл проверяется и в форме показывается <strong>оценка требований к памяти</strong>: <strong>требование NVRAM</strong> (образ кода + постоянные данные, сумма в стиле C6+C8 из GP Card Spec) и оценка RAM. Там же показаны <strong>библиотеки, с которыми слинкован CAP</strong> (его компонент Import: имя библиотеки, AID и минимальная версия <code class="font-mono text-sm">≥ x.y</code> — имена стандартных библиотек и семейства подставляются, а где известно, даётся подсказка о версии Java Card), идентификаторы пакета и апплетов и разбивка по компонентам (размеры и доли в порядке load-файла) — всё сгруппировано по разделам <strong>Package</strong>, <strong>Requires</strong>, <strong>Memory</strong> и <strong>Components</strong>. Повреждённый файл не проходит анализ, и <strong>Execute</strong> остаётся недоступной до успешного анализа.</td></tr>
</tbody>
</table>
+1 -1
View File
@@ -318,7 +318,7 @@
<thead><tr class="border-b border-gray-300 dark:border-slate-700"><th class="text-left py-1 px-2">Operation</th><th class="text-left py-1 px-2">Description</th></tr></thead>
<tbody>
<tr class="border-b border-gray-200 dark:border-slate-700"><td class="py-1 px-2">Explore Card (all GP data)</td><td class="py-1 px-2">Queries GET STATUS for ISD, Applications, ELFs, and ELF Modules, plus GET DATA FF21 for memory info. Results appear in an explorer view with per-item <strong>Delete</strong> buttons.</td></tr>
<tr><td class="py-1 px-2">Install Package (.cap file)</td><td class="py-1 px-2">Sends a <code class="font-mono text-sm">.cap</code> file to the card via the server: INSTALL[for load] &rarr; LOAD &times;N &rarr; INSTALL[for install (+make selectable)]. The load file is split into LOAD APDUs of the <strong>LOAD block size</strong> (1&ndash;240 bytes of payload, 240 by default); each secured packet is delivered as a single SMS or, when larger, as a concatenated SMS-PP download of up to 5 segments, so a large <code class="font-mono text-sm">.cap</code> simply takes several SMS. As soon as the file is selected it is validated and its <strong>memory requirements are estimated</strong> in the form: the <strong>NVRAM requirement</strong> (the code image + persistent data, the GlobalPlatform Card Spec C6+C8 style total) and the RAM estimate. The same box reports the <strong>libraries the CAP is linked against</strong> (its Import component, shown as <code class="font-mono text-sm">name ≥ version</code> with standard library names resolved and a Java Card SDK hint where known), the package/applet identity and the component breakdown (sizes and shares in load-file order) &mdash; grouped under the <strong>Package</strong>, <strong>Requires</strong>, <strong>Memory</strong> and <strong>Components</strong> headings. A corrupt or wrong-format file fails the analysis, and <strong>Execute</strong> stays disabled until a successful analysis.</td></tr>
<tr><td class="py-1 px-2">Install Package (.cap file)</td><td class="py-1 px-2">Sends a <code class="font-mono text-sm">.cap</code> file to the card via the server: INSTALL[for load] &rarr; LOAD &times;N &rarr; INSTALL[for install (+make selectable)]. The load file is split into LOAD APDUs of the <strong>LOAD block size</strong> (1&ndash;240 bytes of payload, 240 by default); each secured packet is delivered as a single SMS or, when larger, as a concatenated SMS-PP download of up to 5 segments, so a large <code class="font-mono text-sm">.cap</code> simply takes several SMS. As soon as the file is selected it is validated and its <strong>memory requirements are estimated</strong> in the form: the <strong>NVRAM requirement</strong> (the code image + persistent data, the GlobalPlatform Card Spec C6+C8 style total) and the RAM estimate. The same box reports the <strong>libraries the CAP is linked against</strong> (its Import component: library name, AID and the minimum version <code class="font-mono text-sm">≥ x.y</code>, with standard names and families resolved and a Java Card SDK hint where known), the package/applet identity and the component breakdown (sizes and shares in load-file order) &mdash; grouped under the <strong>Package</strong>, <strong>Requires</strong>, <strong>Memory</strong> and <strong>Components</strong> headings. A corrupt or wrong-format file fails the analysis, and <strong>Execute</strong> stays disabled until a successful analysis.</td></tr>
</tbody>
</table>
+7 -3
View File
@@ -1616,7 +1616,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.17';
const SIMPLE_VERSION = '3.5.18';
document.getElementById('app-version').textContent = 'v' + SIMPLE_VERSION;
// ===== Tab switching =====
@@ -8083,12 +8083,15 @@ function capMemHtml(mem) {
// ">=" requirements: same major, minor >= the export version (4.5.2);
// refs = distinct constant-pool references to the package (6.7) ──
if (imports.length) {
let reqRows = row(head('', 'text-left') + head(esc(t('Version')), 'pl-4 text-right whitespace-nowrap') +
let reqRows = row(head('', 'text-left') + head(esc(t('AID')), 'pl-4 text-left') +
head(esc(t('Version')), 'pl-4 text-right whitespace-nowrap') +
head(esc(t('Family')), 'pl-4 text-left') + head(esc(t('Refs')), 'pl-4 text-right whitespace-nowrap'));
imports.forEach(im => {
const name = jcAidName(im.aid);
const fam = jcAidFamily(im.aid);
reqRows += row(cell(name ? esc(name) : '<span class="font-mono break-all">' + esc(im.aid) + '</span>', 'whitespace-nowrap') +
// unresolved names show a dash: the AID column carries the identifier
reqRows += row(cell(name ? esc(name) : '\u2014', 'whitespace-nowrap') +
cell(esc(im.aid), 'pl-4 font-mono break-all') +
num('\u2265 ' + (Number(im.major) || 0) + '.' + (Number(im.minor) || 0)) +
part(esc(fam)) + num(String(Number(im.refs) || 0)));
});
@@ -16180,6 +16183,7 @@ const LANG_RU = {
'Compiled': 'Собрано',
'Requires': 'Требуются',
'Family': 'Семейство',
'AID': 'AID',
'Refs': 'Ссылки',
'NVRAM requirement': 'Требование NVRAM',
'code image (load file)': 'образ кода (load-файл)',
+1 -1
View File
@@ -1,4 +1,4 @@
const CACHE = 'simple-v270';
const CACHE = 'simple-v271';
const URLS = [
'index.html',
'help.html',
+10 -10
View File
@@ -106,15 +106,14 @@ test('capMemHtml renders the grouped report: package, requires, memory, componen
assert.ok(out.includes('>24 B</td>'), out);
assert.ok(out.includes('peak method frame 8 B'), out);
assert.ok(out.includes('C6=3000 C7=0x0110 C8=0x0088'), out);
// requires: library name, version, family and refs; the AID is not
// repeated when the name resolves (it adds nothing), and the section
// carries no package AIDs
assert.ok(out.includes('>javacard.framework</td>'), out);
assert.ok(out.includes('>\u2265 1.0</td>'), out);
assert.ok(out.includes('>Oracle JavaCard API</td>'), out);
assert.ok(out.includes('>6</td>'), out);
// requires: library name, AID, minimum version, family and refs
const req = out.slice(out.indexOf('Requires (1)'), out.indexOf('Memory</div>'));
assert.ok(!req.includes('A0000000620101'), req);
assert.ok(req.includes('>AID</td>'), req);
assert.ok(req.includes('>javacard.framework</td>'), req);
assert.ok(req.includes('>A0000000620101</td>'), req);
assert.ok(req.includes('>\u2265 1.0</td>'), req);
assert.ok(req.includes('>Oracle JavaCard API</td>'), req);
assert.ok(req.includes('>6</td>'), req);
// package identity + compiled-against hint derived from the framework version
assert.ok(out.includes('A0000000620101') && out.includes('>v1.0</td>'), out);
assert.ok(out.includes('applet package'), out);
@@ -143,12 +142,13 @@ test('capMemHtml skips the import section on a response without imports', () =>
assert.ok(out.includes('>2.1 kB</td>'), out);
});
test('capMemHtml keeps unknown import AIDs bare and labels the family only when known', () => {
test('capMemHtml shows a dash for an unresolved import name (the AID column carries it)', () => {
const mem = memFixture();
mem.imports = [{ aid: 'A1130001180001', minor: 0, major: 1, refs: 2 }];
const out = capMemHtml(mem);
const req = out.slice(out.indexOf('Requires (1)'), out.indexOf('Memory</div>'));
assert.ok(req.includes('A1130001180001'), req); // unknown AID: shown as the name
assert.ok(req.includes('>\u2014</td>'), req); // no resolved name: a dash
assert.ok(req.includes('>A1130001180001</td>'), req); // the AID column carries it
assert.ok(req.includes('>\u2265 1.0</td>'), req);
assert.ok(req.includes('>2</td>'), req); // refs
assert.ok(!out.includes('Java Card'), out); // no framework import
+1 -1
View File
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project]
name = "pysim-simple-server"
version = "3.5.17"
version = "3.5.18"
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.
+1 -1
View File
@@ -31,7 +31,7 @@ from osmocom.tlv import BER_TLV_IE
from osmocom.utils import rpad
VERSION = '3.5.17'
VERSION = '3.5.18'
MAX_ENVELOPE_SEGMENTS = 5 # max SMS segments for outgoing C-APDU in ENVELOPE