ui: clear stale RAM status on op change, remember the card preset
- ramOpChanged() now clears the previous execution status (result line, steps, explorer, progress) only when the Operation value actually changes, so switching Explore -> Install Package no longer shows the old 'Partial - ELF Modules: no data' until Execute; re-entering the RAM subtab still preserves the last result - ramClearResults() also hides the progress caption - the Card preset dropdown no longer resets to the placeholder after every executed operation (ramSaveCntr -> ramRender rebuilt it) or on subtab re-entry: ramRender keeps the current/remembered index, ramApplyCard and ramExecute remember the selection for the session, and cardsRemove keeps _ramCardIdx aligned via ramCardIdxAfterRemove - tests: op-change clearing, selection preservation across rebuilds, pick/execute remembering, index bookkeeping; SW cache v126 -> v127.
This commit is contained in:
+22
-6
@@ -4806,10 +4806,16 @@ async function pysimSendOta() {
|
|||||||
// reuse /api/send-ota. Install Package sends the .cap hex to /api/ram-install
|
// reuse /api/send-ota. Install Package sends the .cap hex to /api/ram-install
|
||||||
// which orchestrates INSTALL[for load] -> LOAD x N -> INSTALL[for install].
|
// which orchestrates INSTALL[for load] -> LOAD x N -> INSTALL[for install].
|
||||||
|
|
||||||
|
let _ramCardIdx = null;
|
||||||
|
let _ramOpLast = null;
|
||||||
|
|
||||||
function ramRender() {
|
function ramRender() {
|
||||||
// populate the card preset selector from the in-memory cards[] array
|
// populate the card preset selector from the in-memory cards[] array,
|
||||||
|
// keeping the current/remembered selection across rebuilds
|
||||||
const sel = document.getElementById('ram-card-sel');
|
const sel = document.getElementById('ram-card-sel');
|
||||||
if (!sel) return;
|
if (!sel) return;
|
||||||
|
const prev = parseInt(sel.value, 10);
|
||||||
|
const keep = (!isNaN(prev) && cards[prev]) ? prev : _ramCardIdx;
|
||||||
sel.innerHTML = '<option value="" data-l10n="— Select card —">— Select card —</option>';
|
sel.innerHTML = '<option value="" data-l10n="— Select card —">— Select card —</option>';
|
||||||
cards.forEach((c, i) => {
|
cards.forEach((c, i) => {
|
||||||
const opt = document.createElement('option');
|
const opt = document.createElement('option');
|
||||||
@@ -4817,23 +4823,23 @@ function ramRender() {
|
|||||||
opt.textContent = c.name || ('Card ' + i);
|
opt.textContent = c.name || ('Card ' + i);
|
||||||
sel.appendChild(opt);
|
sel.appendChild(opt);
|
||||||
});
|
});
|
||||||
|
if (keep !== null && keep !== undefined && cards[keep]) sel.value = String(keep);
|
||||||
ramOpChanged();
|
ramOpChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
function ramApplyCard(idx) {
|
function ramApplyCard(idx) {
|
||||||
// copy the selected card preset into the SP form fields so that
|
// copy the selected card preset into the SP form fields so that
|
||||||
// getRamSpParams() picks up the right SPI/KIc/KID/TAR/CNTR/keys
|
// getRamSpParams() picks up the right SPI/KIc/KID/TAR/CNTR/keys
|
||||||
|
const i = parseInt(idx, 10);
|
||||||
|
if (!isNaN(i) && cards[i]) _ramCardIdx = i;
|
||||||
cardsApply(idx);
|
cardsApply(idx);
|
||||||
}
|
}
|
||||||
|
|
||||||
function ramOpChanged() {
|
function ramOpChanged() {
|
||||||
const op = document.getElementById('ram-op').value;
|
const op = document.getElementById('ram-op').value;
|
||||||
|
if (_ramOpLast !== null && op !== _ramOpLast) ramClearResults();
|
||||||
|
_ramOpLast = op;
|
||||||
document.getElementById('ram-install-params').classList.toggle('hidden', op !== 'install-cap');
|
document.getElementById('ram-install-params').classList.toggle('hidden', op !== 'install-cap');
|
||||||
if (op !== 'explore') {
|
|
||||||
_ramExplorerData = null;
|
|
||||||
document.getElementById('ram-explorer').classList.add('hidden');
|
|
||||||
document.getElementById('ram-explorer').innerHTML = '';
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function ramShowProgress(text) {
|
function ramShowProgress(text) {
|
||||||
@@ -4848,6 +4854,7 @@ function ramHideProgress() {
|
|||||||
|
|
||||||
function ramClearResults() {
|
function ramClearResults() {
|
||||||
_ramExplorerData = null;
|
_ramExplorerData = null;
|
||||||
|
ramHideProgress();
|
||||||
document.getElementById('ram-result').classList.add('hidden');
|
document.getElementById('ram-result').classList.add('hidden');
|
||||||
document.getElementById('ram-explorer').classList.add('hidden');
|
document.getElementById('ram-explorer').classList.add('hidden');
|
||||||
document.getElementById('ram-explorer').innerHTML = '';
|
document.getElementById('ram-explorer').innerHTML = '';
|
||||||
@@ -5384,6 +5391,8 @@ async function ramInstallCap(sp) {
|
|||||||
|
|
||||||
async function ramExecute() {
|
async function ramExecute() {
|
||||||
ramClearResults();
|
ramClearResults();
|
||||||
|
const cardIdx = parseInt(document.getElementById('ram-card-sel').value, 10);
|
||||||
|
if (!isNaN(cardIdx) && cards[cardIdx]) _ramCardIdx = cardIdx;
|
||||||
const op = document.getElementById('ram-op').value;
|
const op = document.getElementById('ram-op').value;
|
||||||
const sp = getRamSpParams();
|
const sp = getRamSpParams();
|
||||||
if (!sp.kicKey || !sp.kidKey) {
|
if (!sp.kicKey || !sp.kidKey) {
|
||||||
@@ -5589,7 +5598,14 @@ function cardsAdd() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function ramCardIdxAfterRemove(idx, removedIdx) {
|
||||||
|
if (idx === null || idx === undefined || idx < 0) return null;
|
||||||
|
if (idx === removedIdx) return null;
|
||||||
|
return idx > removedIdx ? idx - 1 : idx;
|
||||||
|
}
|
||||||
|
|
||||||
function cardsRemove(i) {
|
function cardsRemove(i) {
|
||||||
|
_ramCardIdx = ramCardIdxAfterRemove(_ramCardIdx, i);
|
||||||
cards.splice(i, 1);
|
cards.splice(i, 1);
|
||||||
cardsSave();
|
cardsSave();
|
||||||
cardsRender();
|
cardsRender();
|
||||||
|
|||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
const CACHE = 'otaman-v126';
|
const CACHE = 'otaman-v127';
|
||||||
const URLS = [
|
const URLS = [
|
||||||
'index.html',
|
'index.html',
|
||||||
'help.html',
|
'help.html',
|
||||||
|
|||||||
+125
-2
@@ -6,7 +6,7 @@ const path = require('node:path');
|
|||||||
const html = fs.readFileSync(path.join(__dirname, '..', 'index.html'), 'utf8');
|
const html = fs.readFileSync(path.join(__dirname, '..', 'index.html'), 'utf8');
|
||||||
|
|
||||||
function extractFunc(src, name) {
|
function extractFunc(src, name) {
|
||||||
const re = new RegExp('function\\s+' + name + '\\s*\\([^)]*\\)\\s*\\{');
|
const re = new RegExp('(?:async\\s+)?function\\s+' + name + '\\s*\\([^)]*\\)\\s*\\{');
|
||||||
const m = re.exec(src);
|
const m = re.exec(src);
|
||||||
if (!m) throw new Error('function ' + name + ' not found');
|
if (!m) throw new Error('function ' + name + ' not found');
|
||||||
let i = m.index + m[0].length - 1;
|
let i = m.index + m[0].length - 1;
|
||||||
@@ -22,7 +22,8 @@ function extractFunc(src, name) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Extract chain builder functions and dependencies
|
// Extract chain builder functions and dependencies
|
||||||
const FNS = ['berLenStr', 'buildApdu', 'escHtml', 'esc', 'chainInit', 'chainRamBuildRowHex', 'ramFmtLifecycle', 'ramFmtPrivileges', 'ramRenderExploreHtml'];
|
const FNS = ['berLenStr', 'buildApdu', 'escHtml', 'esc', 'chainInit', 'chainRamBuildRowHex', 'ramFmtLifecycle', 'ramFmtPrivileges', 'ramRenderExploreHtml',
|
||||||
|
'ramCardIdxAfterRemove', 'ramClearResults', 'ramHideProgress', 'ramOpChanged', 'ramRender', 'ramApplyCard', 'ramExecute'];
|
||||||
let code = '';
|
let code = '';
|
||||||
for (const f of FNS) {
|
for (const f of FNS) {
|
||||||
code += extractFunc(html, f) + '\n';
|
code += extractFunc(html, f) + '\n';
|
||||||
@@ -32,6 +33,8 @@ if (m) code += m[0].replace(/^const /, 'var ') + '\n';
|
|||||||
const lc = html.match(/const RAM_LIFECYCLE = \{[\s\S]*?\n\};/);
|
const lc = html.match(/const RAM_LIFECYCLE = \{[\s\S]*?\n\};/);
|
||||||
if (lc) code += lc[0].replace(/^const /, 'var ') + '\n';
|
if (lc) code += lc[0].replace(/^const /, 'var ') + '\n';
|
||||||
eval(code);
|
eval(code);
|
||||||
|
code += 'var _ramCardIdx = null;\nvar _ramOpLast = null;\nvar _ramExplorerData = null;\n';
|
||||||
|
eval(code);
|
||||||
|
|
||||||
const els = {};
|
const els = {};
|
||||||
const doc = { getElementById: (id) => { if (!els[id]) els[id] = {value:''}; return els[id]; } };
|
const doc = { getElementById: (id) => { if (!els[id]) els[id] = {value:''}; return els[id]; } };
|
||||||
@@ -197,3 +200,123 @@ test('ramFmtPrivileges uses the translated (none) placeholder', () => {
|
|||||||
assert.strictEqual(ramFmtPrivileges('00'), 'XX(none)');
|
assert.strictEqual(ramFmtPrivileges('00'), 'XX(none)');
|
||||||
delete global.t;
|
delete global.t;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function fakeClassList() {
|
||||||
|
const set = new Set();
|
||||||
|
return {
|
||||||
|
add: (...cs) => cs.forEach(c => set.add(c)),
|
||||||
|
remove: (...cs) => cs.forEach(c => set.delete(c)),
|
||||||
|
contains: c => set.has(c),
|
||||||
|
toggle: (c, on) => { if (on === undefined ? !set.has(c) : on) set.add(c); else set.delete(c); },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function fakeEl(id) {
|
||||||
|
return {
|
||||||
|
id,
|
||||||
|
value: '',
|
||||||
|
innerHTML: '',
|
||||||
|
textContent: '',
|
||||||
|
classList: fakeClassList(),
|
||||||
|
options: [],
|
||||||
|
appendChild(opt) { this.options.push(opt); },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function fakeRamDocument(ids) {
|
||||||
|
const els = {};
|
||||||
|
for (const id of ids) els[id] = fakeEl(id);
|
||||||
|
const sel = els['ram-card-sel'];
|
||||||
|
if (sel) {
|
||||||
|
Object.defineProperty(sel, 'innerHTML', {
|
||||||
|
get() { return this._html || ''; },
|
||||||
|
set(v) { this._html = v; this.value = ''; },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
globalThis.document = {
|
||||||
|
getElementById: id => els[id] || null,
|
||||||
|
createElement: () => fakeEl('option'),
|
||||||
|
};
|
||||||
|
return els;
|
||||||
|
}
|
||||||
|
|
||||||
|
test('ramOpChanged clears the executed status only on a real op change', () => {
|
||||||
|
const els = fakeRamDocument(['ram-op', 'ram-install-params', 'ram-result', 'ram-explorer', 'ram-steps', 'ram-progress']);
|
||||||
|
_ramOpLast = null;
|
||||||
|
els['ram-op'].value = 'explore';
|
||||||
|
ramOpChanged();
|
||||||
|
assert.ok(!els['ram-result'].classList.contains('hidden'));
|
||||||
|
els['ram-result'].classList.remove('hidden');
|
||||||
|
els['ram-steps'].classList.remove('hidden');
|
||||||
|
ramOpChanged();
|
||||||
|
assert.ok(!els['ram-result'].classList.contains('hidden'), 'same op must keep the result');
|
||||||
|
els['ram-op'].value = 'install-cap';
|
||||||
|
ramOpChanged();
|
||||||
|
assert.ok(els['ram-result'].classList.contains('hidden'));
|
||||||
|
assert.ok(els['ram-steps'].classList.contains('hidden'));
|
||||||
|
assert.ok(els['ram-explorer'].classList.contains('hidden'));
|
||||||
|
assert.ok(els['ram-progress'].classList.contains('hidden'));
|
||||||
|
assert.ok(!els['ram-install-params'].classList.contains('hidden'));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('ramRender keeps the selected card preset across rebuilds', () => {
|
||||||
|
const els = fakeRamDocument(['ram-card-sel', 'ram-op', 'ram-install-params', 'ram-result', 'ram-explorer', 'ram-steps', 'ram-progress']);
|
||||||
|
globalThis.cards = [{ name: 'A' }, { name: 'B' }, { name: 'C' }];
|
||||||
|
_ramCardIdx = null;
|
||||||
|
_ramOpLast = 'explore';
|
||||||
|
els['ram-op'].value = 'explore';
|
||||||
|
ramRender();
|
||||||
|
assert.strictEqual(els['ram-card-sel'].value, '');
|
||||||
|
els['ram-card-sel'].value = '1';
|
||||||
|
ramRender();
|
||||||
|
assert.strictEqual(els['ram-card-sel'].value, '1');
|
||||||
|
els['ram-card-sel'].value = '';
|
||||||
|
_ramCardIdx = 2;
|
||||||
|
ramRender();
|
||||||
|
assert.strictEqual(els['ram-card-sel'].value, '2');
|
||||||
|
globalThis.cards = [{ name: 'A' }];
|
||||||
|
_ramCardIdx = 2;
|
||||||
|
ramRender();
|
||||||
|
assert.strictEqual(els['ram-card-sel'].value, '');
|
||||||
|
delete globalThis.cards;
|
||||||
|
});
|
||||||
|
|
||||||
|
test('ramApplyCard remembers a valid picked preset', () => {
|
||||||
|
globalThis.cards = [{ name: 'A' }, { name: 'B' }];
|
||||||
|
let applied = null;
|
||||||
|
globalThis.cardsApply = i => { applied = i; };
|
||||||
|
_ramCardIdx = null;
|
||||||
|
ramApplyCard('1');
|
||||||
|
assert.strictEqual(_ramCardIdx, 1);
|
||||||
|
assert.strictEqual(applied, '1');
|
||||||
|
ramApplyCard('');
|
||||||
|
assert.strictEqual(_ramCardIdx, 1, 'invalid pick must not forget the preset');
|
||||||
|
delete globalThis.cards;
|
||||||
|
delete globalThis.cardsApply;
|
||||||
|
});
|
||||||
|
|
||||||
|
test('ramExecute commits the dropdown selection before running', async () => {
|
||||||
|
const els = fakeRamDocument(['ram-card-sel', 'ram-op', 'ram-install-params', 'ram-result', 'ram-explorer', 'ram-steps', 'ram-progress']);
|
||||||
|
globalThis.cards = [{ name: 'A' }];
|
||||||
|
globalThis.getRamSpParams = () => ({ kicKey: '11', kidKey: '22' });
|
||||||
|
let explored = false;
|
||||||
|
globalThis.ramExplore = async () => { explored = true; };
|
||||||
|
globalThis.alert = () => {};
|
||||||
|
_ramCardIdx = null;
|
||||||
|
els['ram-card-sel'].value = '0';
|
||||||
|
els['ram-op'].value = 'explore';
|
||||||
|
await ramExecute();
|
||||||
|
assert.strictEqual(_ramCardIdx, 0);
|
||||||
|
assert.ok(explored);
|
||||||
|
delete globalThis.cards;
|
||||||
|
delete globalThis.getRamSpParams;
|
||||||
|
delete globalThis.ramExplore;
|
||||||
|
delete globalThis.alert;
|
||||||
|
});
|
||||||
|
|
||||||
|
test('ramCardIdxAfterRemove keeps the remembered index aligned', () => {
|
||||||
|
assert.strictEqual(ramCardIdxAfterRemove(2, 0), 1);
|
||||||
|
assert.strictEqual(ramCardIdxAfterRemove(0, 0), null);
|
||||||
|
assert.strictEqual(ramCardIdxAfterRemove(0, 2), 0);
|
||||||
|
assert.strictEqual(ramCardIdxAfterRemove(null, 1), null);
|
||||||
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user