diff --git a/README.md b/README.md index ba43b57..2a75795 100644 --- a/README.md +++ b/README.md @@ -191,9 +191,20 @@ in the stored wire shape and are skipped with a note in the import report. The Trace tab's side panel has a view selector (*Map* / *Stats* / **Files**; a drop-down, since three tab captions no longer fit). The Files view is a trace-wide timeline of file operations — every **Read**, **Update** and -**Changed (card)** row shows the same file name as the Map view, and clicking -a row jumps to the APDU in the trace. A *Changed (card)* row carries a Δ -button that opens the changed ranges with the previous and the new bytes. +**Changed on card** row shows the same file name as the Map view, and clicking +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 +**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 +per row with an ASCII column, never-observed bytes drawn as `--`) with the +bytes that changed highlighted in one colour and the bytes the operation first +observed in another, plus the changed/new byte counts and the APDU reference. + +The images come from `GET /api/sessions//files/image?key=…&change=…` and +are reconstructed from the current image by undoing the later changes +(`filetrack.FileTracker.change_images`) — nothing is duplicated per event. +Every value change is recorded, so the values are exact; a byte that was only +*first observed* after the selected change can appear as known in both views. The data comes from `simtrace_analyser/filetrack.py` (`GET /api/sessions//files`), a single-pass tracker over the decoded diff --git a/README_RUS.md b/README_RUS.md index 6a42a34..b95b16e 100644 --- a/README_RUS.md +++ b/README_RUS.md @@ -196,9 +196,21 @@ Application Layer* — два отдельных поля выбора, имен В боковой панели вкладки «Трассировка» появился выбор вида (*Map* / *Stats* / **Files** — выпадающий список, три подписи-вкладки уже не помещались). Вид Files — это таймлайн файловых операций по всей трассе: строки **Read**, -**Update** и **Changed (card)** показывают те же имена файлов, что и вид Map, -а щелчок по строке переходит к APDU в трассе. У строки *Changed (card)* есть -кнопка Δ, открывающая изменённые диапазоны с прежними и новыми байтами. +**Update** и **Changed on card** показывают те же имена файлов, что и вид Map, +а щелчок по строке переходит к APDU в трассе. У строк с записанным изменением +(изменение со стороны карты либо запись, изменившая или впервые показавшая +байты) есть кнопка **Details**: окно показывает затронутую единицу — файл +целиком или изменившуюся запись — **до и после** изменения в виде hex-вида +(16 байт в строке со столбцом ASCII, никогда не наблюдавшиеся байты — `--`), +причём изменённые байты подсвечены одним цветом, а впервые наблюдаемые — +другим; ниже — счётчики изменённых/новых байт и ссылка на APDU. + +Изображения отдаёт `GET /api/sessions//files/image?key=…&change=…`; они +восстанавливаются из текущего образа отменой более поздних изменений +(`filetrack.FileTracker.change_images`) — поэтому ничего не дублируется по +событиям. Каждое изменение значения записывается, так что значения точны; +байт, который был лишь *впервые показан* после выбранного изменения, может +отображаться известным в обоих видах. Данные даёт `simtrace_analyser/filetrack.py` (`GET /api/sessions//files`) — однопроходный анализатор сессии, который ведёт по ключу (DF, FID) и **через diff --git a/frontend/index.html b/frontend/index.html index 46b3edd..25392c0 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -188,13 +188,25 @@ body { font: 14px/1.5 system-ui, sans-serif; background: var(--bg); color: var(- .file-op-read { color: var(--text-muted); } .file-op-update { color: var(--accent); } .file-op-card { color: var(--warn); } -.file-op .file-op-diff { margin-left: auto; background: none; border: none; color: var(--warn); cursor: pointer; font-size: 12px; padding: 0 2px; flex: 0 0 auto; } -.file-op .file-op-diff:hover { color: var(--accent); } -.file-diff-modal { max-width: 640px; } -.file-diff-body { max-height: 50vh; overflow-y: auto; margin-bottom: 16px; } -.file-diff-body table { font-size: 12px; } -.file-diff-body td { padding: 2px 8px 2px 0; vertical-align: top; } -.file-diff-body .file-diff-hex { font-family: ui-monospace, monospace; word-break: break-all; } +.file-op .file-op-diff { margin-left: auto; background: none; border: none; color: var(--accent); cursor: pointer; font-size: 12px; padding: 0 2px; flex: 0 0 auto; text-decoration: underline; } +.file-op .file-op-diff:hover { color: var(--warn); } +.file-diff-modal { max-width: 760px; } +.file-diff-body { max-height: 60vh; overflow-y: auto; margin-bottom: 16px; } +.hex-legend { display: flex; align-items: center; gap: 6px; font-size: 12px; color: var(--text-muted); margin-bottom: 6px; flex-wrap: wrap; } +.hex-summary { font-size: 12px; color: var(--text-muted); margin-bottom: 10px; } +.hex-pair { display: flex; gap: 16px; flex-wrap: wrap; } +.hex-pair > div { flex: 1 1 320px; min-width: 0; } +.hex-pair h4 { font-size: 12px; text-transform: uppercase; letter-spacing: 1px; color: var(--accent); margin-bottom: 4px; } +.hex-view { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 12px; line-height: 1.45; white-space: pre; overflow-x: auto; } +.hex-row { display: flex; align-items: baseline; } +.hex-addr { color: var(--text-muted); margin-right: 8px; } +.hex-cell { padding: 0 1px; border-radius: 2px; } +.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-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-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:hover { background: var(--bg-input); } .map-files-toggle { color: var(--accent); cursor: pointer; } @@ -438,7 +450,7 @@ body { font: 14px/1.5 system-ui, sans-serif; background: var(--bg); color: var(-