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:
@@ -21,8 +21,11 @@ function extractFunc(src, name) {
|
||||
return src.slice(m.index, i + 1);
|
||||
}
|
||||
|
||||
let code = 'var _pysimCardStateKey = null;\nvar _pysimCardSession = null;\n';
|
||||
let code = 'var _pysimCardStateKey = null;\nvar _pysimCardSession = null;\n'
|
||||
+ 'var _pysimServerAvailable = null;\nvar _pysimCardEquipped = false;\n';
|
||||
code += extractFunc(html, 'pysimCardStateUpdate') + '\n';
|
||||
code += extractFunc(html, 'pysimAvailabilityState') + '\n';
|
||||
code += extractFunc(html, 'pysimControlDisabled') + '\n';
|
||||
code += '\nglobalThis.esc = s => s;\n';
|
||||
code += 'globalThis.t = s => s;\n';
|
||||
eval(code);
|
||||
@@ -32,9 +35,10 @@ function setup() {
|
||||
const calls = { connected: [], resets: [], refreshStatus: [] };
|
||||
_pysimCardStateKey = null;
|
||||
_pysimCardSession = null;
|
||||
globalThis.document = { getElementById: () => el };
|
||||
globalThis.document = { getElementById: () => el, querySelectorAll: () => [] };
|
||||
globalThis.pysimSetConnected = v => calls.connected.push(v);
|
||||
globalThis.pysimResetCardData = refresh => calls.resets.push(refresh);
|
||||
globalThis.pysimApplyAvailability = () => {};
|
||||
return { el, calls };
|
||||
}
|
||||
|
||||
@@ -98,3 +102,21 @@ test('payload without connected flag is ignored', () => {
|
||||
pysimCardStateUpdate(null);
|
||||
assert.deepStrictEqual(calls.connected, []);
|
||||
});
|
||||
|
||||
test('availability state and control gating follow server/card state', () => {
|
||||
_pysimServerAvailable = null;
|
||||
assert.strictEqual(pysimAvailabilityState(), 'server-down');
|
||||
assert.strictEqual(pysimControlDisabled('server', 'server-down'), true);
|
||||
assert.strictEqual(pysimControlDisabled('card', 'server-down'), true);
|
||||
|
||||
_pysimServerAvailable = true;
|
||||
_pysimCardEquipped = false;
|
||||
assert.strictEqual(pysimAvailabilityState(), 'no-card');
|
||||
assert.strictEqual(pysimControlDisabled('server', 'no-card'), false);
|
||||
assert.strictEqual(pysimControlDisabled('card', 'no-card'), true);
|
||||
|
||||
_pysimCardEquipped = true;
|
||||
assert.strictEqual(pysimAvailabilityState(), 'card');
|
||||
assert.strictEqual(pysimControlDisabled('card', 'card'), false);
|
||||
assert.strictEqual(pysimControlDisabled('server', 'card'), false);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user