Files view: drop the ASCII column from the change-details hex views

- the modal's hex views carried a 16-byte ASCII column per row; file data is
  rarely text and the column halved the room for the two before/after views,
  so it is gone: `hexImageRows` no longer builds an `ascii` field,
  `hexImageHtml` no longer emits it and the `.hex-ascii` CSS rule is removed
- tests: the hex-row test asserts the field is gone (so it cannot come back
  silently); README/README_RUS wording updated
- SW cache simtrace-analyser-v67 (cosmetic, no version bump)
This commit is contained in:
catarrh committed 2026-10-17 21:29:31 +03:00
1 parent 407821ab3d
commit cbb73536f4
5 files changed
+11 -16

No files matched your search

+3 -3
View File
@@ -196,9 +196,9 @@ a row jumps to the APDU in the trace. Rows that carry a recorded change (a
card-side change, or an update that wrote or first observed bytes) also offer card-side change, or an update that wrote or first observed bytes) also offer
**Details**: a modal showing the affected unit — the whole file, or the single **Details**: a modal showing the affected unit — the whole file, or the single
record that changed — **before and after** the change, as hex views (16 bytes record that changed — **before and after** the change, as hex views (16 bytes
per row with an ASCII column, never-observed bytes drawn as `--`) with the per row, never-observed bytes drawn as `--`) with the bytes that changed
bytes that changed highlighted in one colour and the bytes the operation first highlighted in one colour and the bytes the operation first observed in
observed in another, plus the changed/new byte counts and the APDU reference. another, plus the changed/new byte counts and the APDU reference.
The images come from `GET /api/sessions/<id>/files/image?key=…&change=…` and The images come from `GET /api/sessions/<id>/files/image?key=…&change=…` and
are reconstructed from the current image by undoing the later changes are reconstructed from the current image by undoing the later changes
+3 -3
View File
@@ -201,9 +201,9 @@ Files — это таймлайн файловых операций по все
(изменение со стороны карты либо запись, изменившая или впервые показавшая (изменение со стороны карты либо запись, изменившая или впервые показавшая
байты) есть кнопка **Details**: окно показывает затронутую единицу — файл байты) есть кнопка **Details**: окно показывает затронутую единицу — файл
целиком или изменившуюся запись — **до и после** изменения в виде hex-вида целиком или изменившуюся запись — **до и после** изменения в виде hex-вида
(16 байт в строке со столбцом ASCII, никогда не наблюдавшиеся байты — `--`), (16 байт в строке, никогда не наблюдавшиеся байты — `--`), причём изменённые
причём изменённые байты подсвечены одним цветом, а впервые наблюдаемые — байты подсвечены одним цветом, а впервые наблюдаемые — другим; ниже —
другим; ниже — счётчики изменённых/новых байт и ссылка на APDU. счётчики изменённых/новых байт и ссылка на APDU.
Изображения отдаёт `GET /api/sessions/<id>/files/image?key=…&change=…`; они Изображения отдаёт `GET /api/sessions/<id>/files/image?key=…&change=…`; они
восстанавливаются из текущего образа отменой более поздних изменений восстанавливаются из текущего образа отменой более поздних изменений
+2 -7
View File
@@ -204,7 +204,6 @@ body { font: 14px/1.5 system-ui, sans-serif; background: var(--bg); color: var(-
.hex-cell.hex-changed { background: var(--warn); color: #1a1a1a; font-weight: 600; } .hex-cell.hex-changed { background: var(--warn); color: #1a1a1a; font-weight: 600; }
.hex-cell.hex-new { background: var(--accent); color: #fff; font-weight: 600; } .hex-cell.hex-new { background: var(--accent); color: #fff; font-weight: 600; }
.hex-cell.hex-unknown { color: var(--text-muted); } .hex-cell.hex-unknown { color: var(--text-muted); }
.hex-ascii { color: var(--text-muted); margin-left: 8px; }
.hex-note { font-size: 12px; color: var(--text-muted); margin-top: 6px; } .hex-note { font-size: 12px; color: var(--text-muted); margin-top: 6px; }
.hex-error { font-size: 12px; color: var(--error); } .hex-error { font-size: 12px; color: var(--error); }
.map-item { padding: 3px 8px; cursor: pointer; border-radius: 4px; font-size: 13px; color: var(--text); word-break: break-word; } .map-item { padding: 3px 8px; cursor: pointer; border-radius: 4px; font-size: 13px; color: var(--text); word-break: break-word; }
@@ -2136,22 +2135,18 @@ function hexImageRows(segments, ranges, observed, limit) {
break; break;
} }
const cells = []; const cells = [];
let ascii = '';
for (let i = 0; i < 16; i++) { for (let i = 0; i < 16; i++) {
const pos = off + i; const pos = off + i;
const hex = bytes.get(pos); const hex = bytes.get(pos);
if (hex === undefined) { if (hex === undefined) {
cells.push({hex: '--', cls: 'hex-unknown'}); cells.push({hex: '--', cls: 'hex-unknown'});
ascii += ' ';
continue; continue;
} }
const cls = covered(ranges, pos) ? 'hex-changed' const cls = covered(ranges, pos) ? 'hex-changed'
: covered(observed, pos) ? 'hex-new' : ''; : covered(observed, pos) ? 'hex-new' : '';
const value = parseInt(hex, 16);
cells.push({hex: hex, cls: cls}); cells.push({hex: hex, cls: cls});
ascii += (value >= 0x20 && value < 0x7f) ? String.fromCharCode(value) : '.';
} }
rows.push({offset: off, cells: cells, ascii: ascii}); rows.push({offset: off, cells: cells});
} }
return {rows: rows, truncated: truncated, end: end}; return {rows: rows, truncated: truncated, end: end};
} }
@@ -2162,7 +2157,7 @@ function hexImageHtml(image, key) {
const body = view.rows.map(r => const body = view.rows.map(r =>
`<div class="hex-row"><span class="hex-addr">${r.offset.toString(16).toUpperCase().padStart(4, '0')}</span>` + `<div class="hex-row"><span class="hex-addr">${r.offset.toString(16).toUpperCase().padStart(4, '0')}</span>` +
r.cells.map(c => `<span class="hex-cell ${c.cls}">${c.hex}</span>`).join('') + r.cells.map(c => `<span class="hex-cell ${c.cls}">${c.hex}</span>`).join('') +
`<span class="hex-ascii">${escapeHtml(r.ascii)}</span></div>` '</div>'
).join('') || `<div class="hex-row hex-unknown">${t('never observed')}</div>`; ).join('') || `<div class="hex-row hex-unknown">${t('never observed')}</div>`;
return `<div class="hex-view">${body}</div>` + return `<div class="hex-view">${body}</div>` +
(view.truncated ? `<div class="hex-note">${t('Showing the first {n} bytes of the image').replace('{n}', 2048)}</div>` : ''); (view.truncated ? `<div class="hex-note">${t('Showing the first {n} bytes of the image').replace('{n}', 2048)}</div>` : '');
+1 -1
View File
@@ -1,4 +1,4 @@
const CACHE = 'simtrace-analyser-v66'; const CACHE = 'simtrace-analyser-v67';
const PRECACHE = [ const PRECACHE = [
'index.html', 'index.html',
+2 -2
View File
@@ -313,8 +313,8 @@ test('Files view: the hex image rows mark changed, new and unknown bytes', () =>
assert.deepEqual(cells[2], {hex: '43', cls: 'hex-changed'}); assert.deepEqual(cells[2], {hex: '43', cls: 'hex-changed'});
assert.deepEqual(cells[4], {hex: '--', cls: 'hex-unknown'}); // the gap assert.deepEqual(cells[4], {hex: '--', cls: 'hex-unknown'}); // the gap
assert.deepEqual(cells[8], {hex: '65', cls: 'hex-new'}); assert.deepEqual(cells[8], {hex: '65', cls: 'hex-new'});
// ASCII column: printable bytes stay, gaps and the unobserved tail are blank. // No ASCII column any more (it wasted the modal's width).
assert.equal(view.rows[0].ascii, 'ABCD ef '); assert.equal(view.rows[0].ascii, undefined);
}); });
test('Files view: the hex image respects the row limit', () => { test('Files view: the hex image respects the row limit', () => {