ui: gate controls by server/card state; move Custom files to Profiler
Availability model: server-down / server-up-no-card / card-equipped. - header shows a symbolic indicator dot (red/yellow/green, tooltip) - a 5s probe tracks /api/version+/api/status while not fully ready; the 2s status poll takes over once connected - controls declare data-needs='server' or 'card'; disabled + tooltip when the state does not satisfy them: equip (server), status/reset, file manager read/save/edit, raw APDU, profile-from-card, new snapshot, profiler list Check card, event Send, RAM install/explore delete, STK menu, Send STATUS, Verify vs pySim (server), pySim execute (server), PLI save/poll toggle (server) - pysimConnect no longer conflates server and card: it records both and Connect stays usable without a card - ramSendOta/ramInstallCap now use pysimFetch instead of raw fetch (relative /api URLs broke custom server URLs) Custom files moved from the Card reader sub-tabs to the Profiler list tabs (Profiles / Card snapshots / Custom files); help/README/AGENTS updated. Tests: availability gating, indicator structure, moved tab. SW cache v103 -> v104.
This commit is contained in:
@@ -921,12 +921,15 @@ test('profilerListSwitch toggles the profiles/snapshots tabs', () => {
|
||||
dataset: { listTab: tab },
|
||||
classList: { _c: new Set(), toggle(c, on) { if (on) this._c.add(c); else this._c.delete(c); } },
|
||||
});
|
||||
const btns = [mkBtn('profiles'), mkBtn('snapshots')];
|
||||
const btns = [mkBtn('profiles'), mkBtn('snapshots'), mkBtn('custom')];
|
||||
const profilesEl = { hidden: false, classList: { toggle(cls, on) { profilesEl.hidden = on; } } };
|
||||
const snapsEl = { hidden: true, classList: { toggle(cls, on) { snapsEl.hidden = on; } } };
|
||||
const customEl = { hidden: true, classList: { toggle(cls, on) { customEl.hidden = on; } } };
|
||||
global.pysimCustomRender = () => {};
|
||||
global.setHelpAnchor = () => {};
|
||||
global.document = {
|
||||
querySelectorAll: sel => (sel === '.profiler-list-tab' ? btns : []),
|
||||
getElementById: id => (id === 'profiler-list-profiles' ? profilesEl : id === 'profiler-list-snapshots' ? snapsEl : null),
|
||||
getElementById: id => (id === 'profiler-list-profiles' ? profilesEl : id === 'profiler-list-snapshots' ? snapsEl : id === 'profiler-list-custom' ? customEl : null),
|
||||
};
|
||||
|
||||
profilerListSwitch('snapshots');
|
||||
@@ -941,7 +944,14 @@ test('profilerListSwitch toggles the profiles/snapshots tabs', () => {
|
||||
assert.ok(btns[0].classList._c.has('bg-blue-600'));
|
||||
assert.ok(btns[1].classList._c.has('bg-gray-200'));
|
||||
|
||||
profilerListSwitch('custom');
|
||||
assert.strictEqual(customEl.hidden, false);
|
||||
assert.strictEqual(profilesEl.hidden, true);
|
||||
assert.strictEqual(snapsEl.hidden, true);
|
||||
|
||||
delete global.document;
|
||||
delete global.pysimCustomRender;
|
||||
delete global.setHelpAnchor;
|
||||
});
|
||||
|
||||
test('profilerScanRefreshOptions re-translates mask labels without touching checkbox state', () => {
|
||||
|
||||
Reference in New Issue
Block a user