feat: clean up the Explore UX (v3.6.45)

- Explore runs inline: no blocking modal (that stays for the install
  operations, which can leave a half-loaded package; Explore leaves the
  card consistent).  A test guards that ramExplore never calls the modal.
- Minimal success line: just OK - the details live in the explorer below;
  error runs keep the detailed 'Partial - ...' line.
- Counts moved into the section headings (Applications / Applet Instances
  (n), Executable Load Files (ELFs) / Packages (n)); the redundant '1 ISD'
  count is gone (P1=80 returns the ISD as a single occurrence, GP
  11.4.2.1).
- The RAM command format line gained a tooltip: compact = TS 102 226
  5.2.1 command string, expanded = the AA/22 scripting template (5.2.1.0),
  detected per operation with a read-only GET STATUS probe.
- The expanded-registry checkbox is relabelled 'Fetch expanded registry
  details (load-file / SD associations)' with a tooltip (extra secured
  packets, card support varies, the fallback is automatic) and is now a
  sticky per-browser preference (simple_ram_expanded).

sw cache simple-v318; tests: 531 Python / 681 Node.
This commit is contained in:
2026-09-29 23:34:42 +03:00
parent 3d4e4e81f8
commit cf9c9b7bab
7 changed files with 82 additions and 19 deletions
+47 -1
View File
@@ -22,7 +22,7 @@ function extractFunc(src, name) {
}
// Extract chain builder functions and dependencies
const FNS = ['berLenStr', 'buildApdu', 'escHtml', 'esc', 'chainInit', 'chainRamBuildRowHex', 'ramFmtLifecycle', 'ramFmtPrivileges', 'ramRenderExploreHtml', 'ramStepLine', 'ramStepComponents', 'ramInstallFailHint', 'ramProbeParse', 'ramGetStatusApdu', 'ramDeleteApdu',
const FNS = ['berLenStr', 'buildApdu', 'escHtml', 'esc', 'chainInit', 'chainRamBuildRowHex', 'ramFmtLifecycle', 'ramFmtPrivileges', 'ramRenderExploreHtml', 'ramStepLine', 'ramStepComponents', 'ramInstallFailHint', 'ramProbeParse', 'ramExpandedDetailsInit', 'ramExpandedDetailsChanged', 'ramGetStatusApdu', 'ramDeleteApdu',
'stkParamsBuild', 'ramRemoteSwOk', 'spPorAccepted', 'ramIncrementCntr', 'ramDeleteFromExplorer', 'ramListingSpi2', 'ramRemoveFromExplorer', 'ramHasInstance', 'ramExpandedQueryApdu',
'_parseRawElfEntry', '_parseRawAppEntry', 'ramParseElfStatus', 'ramParseAppStatus', 'parseTLV', '_parseE3Entry',
'ramCardIdxAfterRemove', 'ramClearResults', 'ramHideProgress', 'ramOpChanged', 'ramRender', 'ramApplyCard', 'ramExecute', 'decodePrivileges', 'ramActionBtn', 'ramCapToolkitMode', 'ramOpProgressText',
@@ -788,3 +788,49 @@ test('ramProbeParse reads AID=version lines', () => {
assert.ok(ramProbeParse('').error);
assert.ok(ramProbeParse(' \n# only comments\n').error);
});
test('ramRenderExploreHtml counts apps and ELFs in the section headings', () => {
global.t = s => s;
const out = ramRenderExploreHtml(null,
[{ aid: 'A1', lifecycle: '07' }],
[{ aid: 'A2', lifecycle: '07' }],
[{ aid: 'E1', lifecycle: '01', moduleAids: [] }]);
delete global.t;
assert.ok(out.includes('Applications / Applet Instances (1)'), out);
assert.ok(out.includes('Executable Load Files (ELFs) / Packages (1)'), out);
// the ISD is a single occurrence (GP 11.4.2.1): no count in its heading
assert.ok(out.includes('ISD (Issuer Security Domain)'), out);
assert.ok(!out.includes('ISD (Issuer Security Domain) ('), out);
});
test('the RAM command format line explains compact vs expanded', () => {
global.t = s => s;
const out = ramRenderExploreHtml({ appCount: 1, freeNV: 1, freeV: 1 }, [], [], [], 'compact');
delete global.t;
assert.ok(out.includes('title="compact = the TS 102 226 5.2.1 command string'), out);
});
test('ramExplore runs inline, without the blocking modal', () => {
const src = extractFunc(html, 'ramExplore');
assert.ok(!/ramOpModal(Begin|Finish|Status)/.test(src),
'Explore must not use the RAM modal (installs only)');
});
test('the expanded-details checkbox is a sticky preference', () => {
const els = fakeRamDocument(['ram-expanded-details']);
const store = {};
globalThis.localStorage = {
getItem: k => (k in store ? store[k] : null),
setItem: (k, v) => { store[k] = String(v); },
};
els['ram-expanded-details'].checked = false;
ramExpandedDetailsInit();
assert.strictEqual(els['ram-expanded-details'].checked, false); // default: off
store['simple_ram_expanded'] = '1';
ramExpandedDetailsInit();
assert.strictEqual(els['ram-expanded-details'].checked, true); // stored choice applied
els['ram-expanded-details'].checked = false;
ramExpandedDetailsChanged();
assert.strictEqual(store['simple_ram_expanded'], '0'); // ticking stores it
delete globalThis.localStorage;
});