ui: rename the SCP81 script templates to Explore ISD / Delete AID (v3.1.3)
'Explore' and 'Delete' were ambiguous next to the other SCP81 actions. - template selector: Explore -> Explore ISD, Delete -> Delete AID (EN/RU); - a new Explore script is named 'Explore ISD'; the Delete template keeps its count-based name (e.g. 'Delete 2 AIDs'); - new scriptKindLabel() maps the stored kinds (unchanged: explore/delete/install/empty, so existing localStorage scripts keep working) to those labels and is now used for the scripts table kind column, which used to print the raw lowercase kind untranslated; - docs: help EN/RU, README/RUS, docs/api.md examples, AGENTS; - sw.js simple-v238.
This commit is contained in:
@@ -338,9 +338,9 @@
|
||||
<p class="text-sm mb-2">Именованные списки APDU хранятся локально (<code class="font-mono text-sm">simple_scripts</code>) и передаются серверу при старте слушателя. Каждый список — последовательность hex C-APDU (по одной в строке; допускаются комментарии <code class="font-mono text-sm">#</code>/<code class="font-mono text-sm">;</code>). Кнопка <strong>Новый</strong> создаёт список из шаблона:</p>
|
||||
<ul class="list-disc list-inside text-sm space-y-1 mb-3">
|
||||
<li><strong>Empty</strong> — начать с пустого списка.</li>
|
||||
<li><strong>Explore</strong> — эталонная административная последовательность (<code class="font-mono text-sm">GET DATA FF21</code>, листинги GET STATUS ISD/ELF/приложений, <code class="font-mono text-sm">GET DATA 0085</code>); длинные листинги автоматически продолжаются страницами <code class="font-mono text-sm">SW 6310/CAFE</code>.</li>
|
||||
<li><strong>Explore ISD</strong> — эталонная административная последовательность (<code class="font-mono text-sm">GET DATA FF21</code>, листинги GET STATUS ISD/ELF/приложений, <code class="font-mono text-sm">GET DATA 0085</code>); длинные листинги автоматически продолжаются страницами <code class="font-mono text-sm">SW 6310/CAFE</code>.</li>
|
||||
<li><strong>Install from .cap</strong> — выберите <code class="font-mono text-sm">.cap</code>, при необходимости SD AID, параметры install/STK и <em>Make selectable</em>; кнопка <strong>Сгенерировать</strong> строит INSTALL [for load] → LOAD ×N → INSTALL [for install]. Файл используется только для генерации APDU — он не сохраняется, как и его имя.</li>
|
||||
<li><strong>Delete</strong> — список AID (по одному в строке) и P2 (<em>object only</em> / <em>object and related objects</em>) → APDU DELETE.</li>
|
||||
<li><strong>Delete AID</strong> — список AID (по одному в строке) и P2 (<em>object only</em> / <em>object and related objects</em>) → APDU DELETE.</li>
|
||||
</ul>
|
||||
<p class="text-sm mb-3">Таблица показывает имя, тип, число APDU и время создания каждого скрипта, а также кнопки <strong>Редактировать</strong> и <strong>Удалить</strong>; в редакторе есть поле имени и текстовая область APDU. В ходе сессии сервер отдаёт по одному C-APDU на каждый POST карты и отслеживает выполнение: карта сообщает статус в следующем POST (<code class="font-mono text-sm">X-Admin-Script-Status</code>), оборвавшаяся сессия досылает только невыполненные APDU (<code class="font-mono text-sm">X-Admin-Resume</code> продолжает, новый диалог начинает заново), а завершённый скрипт закрывается ответом <code class="font-mono text-sm">204 No Content</code>. Конструктор RAM/GP вкладки Remote APDU может отправить цепочку команд прямо в прогон кнопкой <strong>«В очередь SCP81»</strong>.</p>
|
||||
</section>
|
||||
|
||||
+2
-2
@@ -338,9 +338,9 @@
|
||||
<p class="text-sm mb-2">Named APDU lists stored locally (<code class="font-mono text-sm">simple_scripts</code>) and sent to the server when a listener starts. Each list is a sequence of hex C-APDUs (one per line; <code class="font-mono text-sm">#</code>/<code class="font-mono text-sm">;</code> comments allowed). <strong>New</strong> creates one from a template:</p>
|
||||
<ul class="list-disc list-inside text-sm space-y-1 mb-3">
|
||||
<li><strong>Empty</strong> — start from scratch.</li>
|
||||
<li><strong>Explore</strong> — the reference administration sequence (<code class="font-mono text-sm">GET DATA FF21</code>, GET STATUS ISD/ELF/application listings, <code class="font-mono text-sm">GET DATA 0085</code>); long listings auto-continue through the <code class="font-mono text-sm">SW 6310/CAFE</code> pages.</li>
|
||||
<li><strong>Explore ISD</strong> — the reference administration sequence (<code class="font-mono text-sm">GET DATA FF21</code>, GET STATUS ISD/ELF/application listings, <code class="font-mono text-sm">GET DATA 0085</code>); long listings auto-continue through the <code class="font-mono text-sm">SW 6310/CAFE</code> pages.</li>
|
||||
<li><strong>Install from .cap</strong> — pick a <code class="font-mono text-sm">.cap</code> plus optional SD AID, install/STK parameters and <em>make selectable</em>; <strong>Generate</strong> builds INSTALL [for load] → LOAD ×N → INSTALL [for install]. The file is only used to generate the APDUs — it is not stored, not even its name.</li>
|
||||
<li><strong>Delete</strong> — AID list (one per line) and P2 (<em>object only</em> / <em>object and related objects</em>) → DELETE APDUs.</li>
|
||||
<li><strong>Delete AID</strong> — AID list (one per line) and P2 (<em>object only</em> / <em>object and related objects</em>) → DELETE APDUs.</li>
|
||||
</ul>
|
||||
<p class="text-sm mb-3">The table lists each script with its kind, APDU count and creation time, plus <strong>Edit</strong> and <strong>Delete</strong>; the editor has a name field and the APDU textarea. Over a session the server serves one C-APDU per card POST and tracks execution: the card reports status in its next POST (<code class="font-mono text-sm">X-Admin-Script-Status</code>), a session that dies resends only the unexecuted APDUs (<code class="font-mono text-sm">X-Admin-Resume</code> continues, a fresh dialog restarts), and a completed script is closed with <code class="font-mono text-sm">204 No Content</code>. The Remote APDU tab's RAM/GP builder can feed a command chain straight into the run with <strong>Queue in SCP81</strong>.</p>
|
||||
</section>
|
||||
|
||||
+17
-6
@@ -1180,9 +1180,9 @@
|
||||
<label class="block mb-1 text-xs font-medium text-gray-600 dark:text-slate-400" data-l10n="New script from">New script from</label>
|
||||
<select id="scripts-template" onchange="scriptsTemplateChanged()" class="border border-gray-300 dark:border-slate-600 text-sm rounded px-2 py-1.5 dark:bg-slate-800">
|
||||
<option value="empty" data-l10n="Empty">Empty</option>
|
||||
<option value="explore" data-l10n="Explore">Explore</option>
|
||||
<option value="explore" data-l10n="Explore ISD">Explore ISD</option>
|
||||
<option value="install" data-l10n="Install from .cap">Install from .cap</option>
|
||||
<option value="delete" data-l10n="Delete">Delete</option>
|
||||
<option value="delete" data-l10n="Delete AID">Delete AID</option>
|
||||
</select>
|
||||
</div>
|
||||
<button id="scripts-new-btn" onclick="scriptsNew()" class="px-3 py-1.5 text-sm rounded bg-blue-600 text-white hover:bg-blue-700" data-l10n="New">New</button>
|
||||
@@ -1436,7 +1436,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.1.2';
|
||||
const SIMPLE_VERSION = '3.1.3';
|
||||
document.getElementById('app-version').textContent = 'v' + SIMPLE_VERSION;
|
||||
|
||||
// ===== Tab switching =====
|
||||
@@ -9472,7 +9472,7 @@ function scriptsNew() {
|
||||
const tpl = document.getElementById('scripts-template').value;
|
||||
scriptsMsg('');
|
||||
if (tpl === 'empty') { scriptsEdit(null, 'empty', '', []); return; }
|
||||
if (tpl === 'explore') { scriptsEdit(null, 'explore', 'Explore', SCP81_EXPLORE_APDUS.slice()); return; }
|
||||
if (tpl === 'explore') { scriptsEdit(null, 'explore', 'Explore ISD', SCP81_EXPLORE_APDUS.slice()); return; }
|
||||
// Install/delete: fill the parameter form above, then press Generate.
|
||||
scriptsTemplateChanged();
|
||||
}
|
||||
@@ -9547,6 +9547,16 @@ function scriptsDelete(id) {
|
||||
scriptsPersist();
|
||||
}
|
||||
|
||||
// Script template kind -> display label. The stored kind stays
|
||||
// 'explore'/'delete'/'install'/'empty' (existing localStorage scripts).
|
||||
function scriptKindLabel(kind) {
|
||||
const k = String(kind || '').toLowerCase();
|
||||
if (k === 'explore') return t('Explore ISD');
|
||||
if (k === 'delete') return t('Delete AID');
|
||||
if (k === 'install') return t('Install from .cap');
|
||||
return t('Empty');
|
||||
}
|
||||
|
||||
function scriptsRender() {
|
||||
const tbody = document.getElementById('scripts-tbody');
|
||||
if (!tbody) return;
|
||||
@@ -9558,7 +9568,7 @@ function scriptsRender() {
|
||||
for (const s of scripts) {
|
||||
html += '<tr class="border-b border-gray-100 dark:border-slate-700">';
|
||||
html += '<td class="py-1 px-2 font-medium">' + esc(s.name) + '</td>';
|
||||
html += '<td class="py-1 px-2 text-xs text-gray-500 dark:text-slate-400">' + esc(t(s.kind || 'empty')) + '</td>';
|
||||
html += '<td class="py-1 px-2 text-xs text-gray-500 dark:text-slate-400">' + esc(scriptKindLabel(s.kind)) + '</td>';
|
||||
html += '<td class="py-1 px-2 font-mono text-xs">' + ((s.apdus || []).length) + '</td>';
|
||||
html += '<td class="py-1 px-2 text-xs">' + esc((s.created || '').slice(0, 10)) + '</td>';
|
||||
html += '<td class="py-1 px-2 whitespace-nowrap">' +
|
||||
@@ -13909,7 +13919,8 @@ const LANG_RU = {
|
||||
'Listener': 'Слушатель',
|
||||
'Scripts': 'Скрипты',
|
||||
'Empty': 'Пустой',
|
||||
'Explore': 'Обзор',
|
||||
'Explore ISD': 'Обзор ISD',
|
||||
'Delete AID': 'Удалить AID',
|
||||
'New': 'Новый',
|
||||
'New script from': 'Новый скрипт из',
|
||||
'Generate': 'Сгенерировать',
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
const CACHE = 'simple-v237';
|
||||
const CACHE = 'simple-v238';
|
||||
const URLS = [
|
||||
'index.html',
|
||||
'help.html',
|
||||
|
||||
@@ -31,11 +31,20 @@ eval(extractFunc(html, 'scp81LogLine'));
|
||||
eval(extractFunc(html, 'scp81LogEntryHtml'));
|
||||
eval(extractFunc(html, 'scp81GroupResults'));
|
||||
eval(extractFunc(html, 'scp81ScriptStateText'));
|
||||
eval(extractFunc(html, 'scriptKindLabel'));
|
||||
global.esc = (s) => String(s == null ? '' : s)
|
||||
.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"');
|
||||
|
||||
test('explore template is the reference administration sequence', () => {
|
||||
const m = /const SCP81_EXPLORE_APDUS = \[(.*?)\];/s.exec(html);
|
||||
test('scriptKindLabel maps the stored kinds to the template labels', () => {
|
||||
assert.strictEqual(scriptKindLabel('explore'), 'Explore ISD');
|
||||
assert.strictEqual(scriptKindLabel('Explore'), 'Explore ISD');
|
||||
assert.strictEqual(scriptKindLabel('delete'), 'Delete AID');
|
||||
assert.strictEqual(scriptKindLabel('install'), 'Install from .cap');
|
||||
assert.strictEqual(scriptKindLabel('empty'), 'Empty');
|
||||
assert.strictEqual(scriptKindLabel(undefined), 'Empty');
|
||||
});
|
||||
|
||||
test('explore template is the reference administration sequence', () => { const m = /const SCP81_EXPLORE_APDUS = \[(.*?)\];/s.exec(html);
|
||||
assert.ok(m, 'SCP81_EXPLORE_APDUS not found');
|
||||
const apdus = [...m[1].matchAll(/'([0-9A-F]+)'/g)].map(x => x[1]);
|
||||
assert.deepStrictEqual(apdus, ['80CAFF2100', '80F28002024F0000', '80CA008500',
|
||||
|
||||
Reference in New Issue
Block a user