Files view: the update row's change link is "Show diff" (v2.6.5)

- an update row whose image changed (or whose bytes were first observed)
  offered a generic "Details" link; it now says "Show diff" — Russian
  «Изменения» — while a card-side change keeps its "Changed on card!" link
  (both open the same change modal)
- the now-unused 'Details' RU key is gone; tests update the wiring pin and
  the Russian-translation list; 527 Python + 61 Node tests
- README/README_RUS mention the new label; PWA_VERSION/pyproject/version.py
  2.6.5, SW cache simtrace-analyser-v79
This commit is contained in:
catarrh committed 2026-10-17 23:41:28 +03:00
1 parent be75be9ed2
commit 009fcf2253
8 files changed
+13 -13

No files matched your search

+1 -1
View File
@@ -211,7 +211,7 @@ until another one is focused (the APDU list keeps the reader's position:
new messages pin the view to the bottom only while the reader is already new messages pin the view to the bottom only while the reader is already
there, so a jump is never pulled away or un-highlighted by the live poll). Rows that carry a recorded change (a there, so a jump is never pulled away or un-highlighted by the live poll). 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 full file path (`MF/EF_ICCID (2FE2)`, **Show diff**: a modal showing the full file path (`MF/EF_ICCID (2FE2)`,
`ADF/EF_IMSI (6F07)`, `DF_GSM_ACCESS/EF_KcGPRS (4F52)`, `AID …/4F09` — the DF `ADF/EF_IMSI (6F07)`, `DF_GSM_ACCESS/EF_KcGPRS (4F52)`, `AID …/4F09` — the DF
named from the EF.DIR labels when known) and the affected unit — the whole named from the EF.DIR labels when known) and the affected unit — the whole
file, or the single record that changed — **before and after** the change, as file, or the single record that changed — **before and after** the change, as
+1 -1
View File
@@ -215,7 +215,7 @@ Files — это таймлайн файловых операций по все
новые сообщения прижимают вид к низу, только пока читатель уже внизу, новые сообщения прижимают вид к низу, только пока читатель уже внизу,
поэтому переход не срывается живым опросом и не снимает выделение). У строк с записанным изменением поэтому переход не срывается живым опросом и не снимает выделение). У строк с записанным изменением
(изменение со стороны карты либо запись, изменившая или впервые показавшая (изменение со стороны карты либо запись, изменившая или впервые показавшая
байты) есть кнопка **Details**: окно показывает полный путь файла (`MF/EF_ICCID (2FE2)`, байты) есть кнопка **Show diff** («Изменения»): окно показывает полный путь файла (`MF/EF_ICCID (2FE2)`,
`ADF/EF_IMSI (6F07)`, `DF_GSM_ACCESS/EF_KcGPRS (4F52)`, `AID …/4F09` — DF `ADF/EF_IMSI (6F07)`, `DF_GSM_ACCESS/EF_KcGPRS (4F52)`, `AID …/4F09` — DF
называется по меткам из EF.DIR, если они есть) и затронутую единицу — файл называется по меткам из EF.DIR, если они есть) и затронутую единицу — файл
целиком или изменившуюся запись — **до и после** изменения в виде hex-вида целиком или изменившуюся запись — **до и после** изменения в виде hex-вида
+3 -3
View File
@@ -2234,7 +2234,7 @@ async function renderFileView() {
} }
view.innerHTML = timeline.map(op => { view.innerHTML = timeline.map(op => {
const diff = op.change_ref const diff = op.change_ref
? `<button class="file-op-diff" data-diff="${escapeHtml(op.change_ref.file)}|${op.change_ref.idx}" title="${t('Show the change')}">${op.kind === 'card_change' ? t('Changed on card!') : t('Details')}</button>` ? `<button class="file-op-diff" data-diff="${escapeHtml(op.change_ref.file)}|${op.change_ref.idx}" title="${t('Show the change')}">${op.kind === 'card_change' ? t('Changed on card!') : t('Show diff')}</button>`
: ''; : '';
return `<div class="file-op ${fileOpClass(op.kind)}" data-idx="${op.index}"> return `<div class="file-op ${fileOpClass(op.kind)}" data-idx="${op.index}">
<span>${escapeHtml(fileOpLabel(op))}</span>${diff} <span>${escapeHtml(fileOpLabel(op))}</span>${diff}
@@ -3226,7 +3226,7 @@ function onFilterChange() {
} }
// ──────────────────── Versioning ──────────────────── // ──────────────────── Versioning ────────────────────
const PWA_VERSION = '2.6.4'; const PWA_VERSION = '2.6.5';
const MIN_SERVER_VERSION = '2.3.0'; const MIN_SERVER_VERSION = '2.3.0';
function parseVersion(v) { function parseVersion(v) {
@@ -3273,8 +3273,8 @@ const LANG_RU = {
'Read': 'Чтение', 'Read': 'Чтение',
'Update': 'Запись', 'Update': 'Запись',
'Changed on card!': 'Изменено на карте!', 'Changed on card!': 'Изменено на карте!',
'Show diff': 'Изменения',
'Change details': 'Подробности изменения', 'Change details': 'Подробности изменения',
'Details': 'Подробнее',
'changed': 'изменено', 'changed': 'изменено',
'newly observed': 'впервые наблюдается', 'newly observed': 'впервые наблюдается',
'never observed': 'не наблюдалось', 'never observed': 'не наблюдалось',
+1 -1
View File
@@ -1,4 +1,4 @@
const CACHE = 'simtrace-analyser-v78'; const CACHE = 'simtrace-analyser-v79';
const PRECACHE = [ const PRECACHE = [
'index.html', 'index.html',
+4 -4
View File
@@ -342,9 +342,9 @@ test('Files view: the view selector (topbar) and the timeline are wired', () =>
assert.match(src, /\$on\('side-view-select', 'change'/); assert.match(src, /\$on\('side-view-select', 'change'/);
assert.match(src, /\$on\('file-view', 'click'/); assert.match(src, /\$on\('file-view', 'click'/);
assert.match(src, /API\.getSessionFiles\(sid\)/); assert.match(src, /API\.getSessionFiles\(sid\)/);
// The trailing link: "Changed on card!" for a card-side change (it opens // The trailing link: "Changed on card!" for a card-side change and
// the change modal), the generic "Details" for an update row. // "Show diff" for an update row — both open the change modal.
assert.match(src, />\$\{op\.kind === 'card_change' \? t\('Changed on card!'\) : t\('Details'\)\}<\/button>/); assert.match(src, />\$\{op\.kind === 'card_change' \? t\('Changed on card!'\) : t\('Show diff'\)\}<\/button>/);
assert.match(src, /API\.getSessionFileImage\(STATE\.sessionViewId, fileKey, idx\)/); assert.match(src, /API\.getSessionFileImage\(STATE\.sessionViewId, fileKey, idx\)/);
assert.match(src, /getSessionFileImage\(id, key, change\)/); assert.match(src, /getSessionFileImage\(id, key, change\)/);
assert.match(src, /files\/image\?key=/); assert.match(src, /files\/image\?key=/);
@@ -359,7 +359,7 @@ test('Files view: strings carry Russian translations', () => {
keys.add(m[1].replace(/\\(['\\])/g, '$1')); keys.add(m[1].replace(/\\(['\\])/g, '$1'));
} }
for (const s of ['Files', 'Read', 'Update', 'Changed on card!', 'Change details', for (const s of ['Files', 'Read', 'Update', 'Changed on card!', 'Change details',
'Details', 'Close', 'Offset', 'Record', 'Before', 'After', 'Show the change', 'Show diff', 'Close', 'Offset', 'Record', 'Before', 'After', 'Show the change',
'changed', 'newly observed', 'never observed', 'changed', 'newly observed', 'never observed',
'{c} bytes changed, {n} bytes newly observed', '{c} bytes changed, {n} bytes newly observed',
'Showing the first {n} bytes of the image', 'Showing the first {n} bytes of the image',
+1 -1
View File
@@ -4,7 +4,7 @@ build-backend = "setuptools.backends._legacy:_Backend"
[project] [project]
name = "simtrace-analyser" name = "simtrace-analyser"
version = "2.6.4" version = "2.6.5"
description = "SIMtrace Analyser — APDU trace analyser for GSMTAP/PCAP captures (bundles the simtrace2-pysniff utility)" description = "SIMtrace Analyser — APDU trace analyser for GSMTAP/PCAP captures (bundles the simtrace2-pysniff utility)"
readme = {file = "README.md", content-type = "text/markdown"} readme = {file = "README.md", content-type = "text/markdown"}
license = {text = "GPL-2.0-or-later"} license = {text = "GPL-2.0-or-later"}
+1 -1
View File
@@ -1,3 +1,3 @@
"""Single source of truth for the SIMtrace Analyser version.""" """Single source of truth for the SIMtrace Analyser version."""
__version__ = '2.6.4' __version__ = '2.6.5'
+1 -1
View File
@@ -224,7 +224,7 @@ class TestImportEndpoint(unittest.TestCase):
class TestVersion(unittest.TestCase): class TestVersion(unittest.TestCase):
def test_package_version(self): def test_package_version(self):
from simtrace2_pysniff import __version__ from simtrace2_pysniff import __version__
self.assertEqual(__version__, '2.6.4') self.assertEqual(__version__, '2.6.5')
def test_status_includes_version(self): def test_status_includes_version(self):
import os import os