ui: keep the selected file in state, drop the File: title line

The File: EF.DIR [LF] line doubled as the selection store: pysimFsClickFile
wrote a formatted display string there and pysimFsRead/Save parsed it back
(stripping '✗ ' and ' [TYPE]'). The pane now keeps the name in
pysimFsSelected, the redundant title is gone (FID/type/size/FCI remain in
the info block), and the dead pysimFsSelect() plus the unused 'File:' RU
entry are removed. Read/Save still re-select on the server first: pySim's
current selection is a shared cursor that tree expansion, scans and
commands move, so it cannot identify the file shown in the pane.

SW cache v121 -> v122; structural test updated.
This commit is contained in:
2026-09-12 22:12:25 +03:00
parent e0e86e31af
commit 2a559371ae
3 changed files with 13 additions and 24 deletions
+5 -20
View File
@@ -866,7 +866,6 @@
</div>
<div class="flex-1">
<div id="pysim-fs-detail" class="hidden">
<div class="text-xs font-mono mb-1"><span data-l10n="File:">File:</span> <b id="pysim-fs-filename"></b></div>
<div id="pysim-fs-info" class="mb-1"></div>
<div id="pysim-fs-content" class="w-full font-mono text-xs border border-gray-300 dark:border-slate-600 rounded px-2 py-1.5 bg-gray-100 dark:bg-slate-800 min-h-20 overflow-y-auto"></div>
<div class="flex gap-2 mt-1 items-center">
@@ -6125,6 +6124,8 @@ async function pysimFsToggleDir(name) {
if (node.expanded) pysimRefresh();
}
let pysimFsSelected = null;
function pysimFsInfoHtml(sel) {
if (!sel) return '';
const num = v => (v === null || v === undefined) ? null : String(v);
@@ -6153,10 +6154,7 @@ async function pysimFsClickFile(name) {
if (!exists) node.children = [];
pysimFsRenderTree();
}
const ft = exists ? sel.file_type : null;
const label = ({ transparent: 'TR', linear_fixed: 'LF', cyclic: 'CY', ber_tlv: 'BT', df: 'DF' })[ft] || '';
const prefix = exists ? '' : '✗ ';
document.getElementById('pysim-fs-filename').textContent = prefix + name + (label ? ' [' + label + ']' : '');
pysimFsSelected = name;
document.getElementById('pysim-fs-info').innerHTML = exists ? pysimFsInfoHtml(sel) : '';
document.getElementById('pysim-fs-content').innerHTML = '';
document.getElementById('pysim-fs-status').textContent = exists ? '' : '✗ File not found on card';
@@ -6169,7 +6167,7 @@ async function pysimFsClickFile(name) {
}
async function pysimFsRead() {
const rawName = document.getElementById('pysim-fs-filename').textContent.replace(/ \[.*?\]$/, '').replace(/^✗ /, '');
const rawName = pysimFsSelected;
const out = document.getElementById('pysim-fs-content');
const statusEl = document.getElementById('pysim-fs-status');
out.innerHTML = '';
@@ -6204,18 +6202,6 @@ async function pysimFsRead() {
}
}
async function pysimFsSelect() {
const name = document.getElementById('pysim-fs-filename').textContent;
const node = pysimFsFindNode(name, pysimFsTreeRoot);
if (node && node.fid) {
await pysimFetch('/api/command', { cmd: 'select ' + node.fid });
} else {
await pysimFetch('/api/command', { cmd: 'select ' + name });
}
pysimFsRefresh();
pysimRefresh();
}
let pysimFsEditMode = false;
let pysimFsEditData = null;
@@ -6249,7 +6235,7 @@ function pysimFsCancel() {
}
async function pysimFsSave() {
const rawName = document.getElementById('pysim-fs-filename').textContent.replace(/ \[.*?\]$/, '').replace(/^✗ /, '');
const rawName = pysimFsSelected;
const statusEl = document.getElementById('pysim-fs-status');
const out = document.getElementById('pysim-fs-content');
const node = pysimFsFindNode(rawName, pysimFsTreeRoot);
@@ -9220,7 +9206,6 @@ const LANG_RU = {
'Execute': 'Выполнить',
'Send': 'Отправить',
'Custom files are added in file manager tree and included in card scan.': 'Пользовательские файлы добавляются в менеджер файлов и учитываются при чтении карт.',
'File:': 'Файл:',
'Cancel': 'Отмена',
'Timeout': 'Таймаут',
'OK': 'OK',
+1 -1
View File
@@ -1,4 +1,4 @@
const CACHE = 'otaman-v121';
const CACHE = 'otaman-v122';
const URLS = [
'index.html',
'help.html',
+7 -3
View File
@@ -69,11 +69,15 @@ test('file manager has a probe-all-files button and status line', () => {
assert.ok(html.includes('id="pysim-fs-probe-status"'));
});
test('file manager shows decoded FCI info under the file name', () => {
const name = html.indexOf('id="pysim-fs-filename"');
test('file manager shows FCI info and keeps the selection in state, not the DOM', () => {
const detail = html.indexOf('id="pysim-fs-detail"');
const info = html.indexOf('id="pysim-fs-info"');
const content = html.indexOf('id="pysim-fs-content"');
assert.ok(name !== -1 && info > name && info < content, 'pysim-fs-info must sit between filename and content');
assert.ok(detail !== -1 && info > detail && info < content, 'pysim-fs-info must sit above the content');
assert.ok(html.includes('function pysimFsInfoHtml'));
assert.ok(html.includes("pysimFsInfoHtml(sel)"));
assert.ok(!html.includes('pysim-fs-filename'));
assert.ok(html.includes('let pysimFsSelected = null;'));
assert.ok(html.includes('pysimFsSelected = name;'));
assert.ok(!html.includes('pysimFsSelect()'));
});