Commit Graph

10 Commits

Author SHA1 Message Date
catarrh 9a688207ba v3.30.4: the in-app dialogs (native alert/confirm/prompt replaced)
The native browser dialogs block the page and look foreign inside the
app.  One shared modal (#pysim-dialog-modal) with the promise-based
helpers pysimAlert/pysimConfirm/pysimPrompt replaces all 71 call sites
(60 alert, 10 confirm, 1 prompt in profilerNew); deferredPrompt.prompt()
is the PWA install prompt and is untouched.

The helpers serialize through ONE promise queue (overlapping dialogs
appear in order, no message is overwritten).  Escape cancels; Enter
accepts except with the Cancel button focused (native behaviour); key
repeats/IME are ignored; a POINTER answer within 500 ms of a dialog
opening is dropped (the buttons keep the same position, so a
double-click's second click would otherwise answer the next queued
dialog - e.g. an alert queued before a destructive confirm).  Backdrop
clicks are ignored.  The dialog is role="dialog" aria-modal="true" and
traps Tab (the handler cycles the controls the dialog shows: alert OK;
confirm Cancel/OK; prompt Cancel/input/OK), so the focus can neither
leave the modal nor reach a background control (Space would activate
it).  When the modal is absent the helpers fall back to the native
dialogs - the Node tests extract single functions without the markup.

Alerts are fire-and-forget (a notification, not a decision); the 10
confirms and the prompt are awaited, which made five functions async
(scriptsDelete, pysimCustomRemove, profilerNew, profilerDelete,
snapshotDelete).  Because the modal no longer freezes the page, the
delete flows that mutated by index after the confirmation mutate by
identity now (profilerDelete/snapshotDelete/pysimCustomRemove/
testSuiteDeleteScript/testSuiteDelete): a list reorder during the open
dialog would otherwise delete the wrong entry.

The nine dialog messages that were still raw English literals are
t()-wrapped - three of them (No secured packet to verify/send, Card
reader server is not connected) activated RU entries that had existed
unused because they were never wrapped; Import error and Snapshot save
failed are new keys.

Drive-by: the batch modal's #ram-op-batch-info used whitespace-pre-line,
which has no rule in the prebuilt style.css (the v3.30.0 stale-style
trap), so its notice never line-broke - now whitespace-pre-wrap.

Tests: a new frontend/tests/dialogs.test.js (15 tests: the modal path,
the queue, the double-answer guard, the keyboard, the grace, the trap,
the fallback, the sweep pin, the stylesheet pin) plus the async/helper
updates in 8 existing test files.  945 frontend + 952 Python;
sw cache simple-v454.
2026-10-18 00:30:06 +03:00
catarrh 3db6f59e69 v3.30.2: the batch RU wording sweep completed
The v3.30.1 review follow-up.  The sweep fixed the two batch-state
strings but two more shared (kind-independent) batch strings were still
install-specific, so a delete/reinstall batch started under an "install"
label:

- 'Start batch' was «Запустить пакетную установку» - the ram-batch-start
  button starts every kind - now «Запустить пакетную операцию» (aligning
  with the optgroup «Пакетные операции (по картам)»).
- the shared panel hint's «...перезапустите пакетную установку» is now
  «...перезапустите пакетную операцию» (the EN was already neutral).

The RU help quoted the old button label: its install row now says
«Запустить пакетную операцию не требует карты», and the row's other
generic "the batch" statements (never retries, the test-run exclusion)
are neutral too; the genuinely install-specific ones (the workflow, the
shared install form) stay.

The v3.30.1 notes were corrected: the counts labels do follow the last
run's kind after a clear (the values stay zero) - the AGENTS entry and
the _ramBatchLastKind/ramBatchRender comments say so now.  The
cleared-title test seeds the state before opening the modal (no reliance
on the previous test's sandbox leftovers).

930 frontend + 945 Python; sw cache simple-v452.
2026-10-15 09:41:29 +03:00
catarrh 19dc0c7d56 v3.30.1: the batch status modal cosmetics
The v3.30.0 review follow-up, both items cosmetic:

- The modal's title follows the run's kind, but a cleared report carries
  kind: null, so Clear flipped a delete/reinstall run to the install
  title.  The last seen kind is remembered (_ramBatchLastKind, set in
  ramBatchRender) and the title falls back to it through the new
  ramBatchModalTitle() helper.
- The two batch-state RU strings stopped being install-specific: 'Batch
  stopped'/'Batch finished' are now «Пакетная операция
  остановлена/завершена» (the v3.29.0 kinds can delete or reinstall),
  matching the help's own «Пакетная операция» wording.

Test: the cleared title keeps the kind and a fresh page keeps the
install default.  930 frontend + 945 Python; sw cache simple-v451.
2026-10-15 01:05:54 +03:00
catarrh 920ce2646b v3.30.0: the batch status modal
A running batch now shows in a modal window (the same chrome as the
single-operation modal): the live status on top - the current step from
ram_progress (Step n/N - <step>, with the bar) or the waiting prompt
("Insert next card. Waiting for a card...") - plus the counts and the
last finished card's verdict, and the processed cards below (the same
attempts table with the per-step verdicts).

It opens on Start and on a page load with a run in flight (the first
poll sees ram_batch.active); Close is disabled until the run ends (the
run is server-side and survives a reload - that is what reopens the
window), and Stop/Clear live inside it (Clear only once the run ended).
The panel's new Show status button reopens a finished report.  A single
operation takes the modal back; ramBatchRender feeds both the panel and
the modal (the poll's fetch decision now runs for either).

Docs: help EN/RU, the repo AGENTS.  Tests: the open on Start and on the
poll, the live status/bar/counts/attempts, the close guard (active vs
ended), the reopen button.  929 frontend + 945 Python; sw cache
simple-v450.
2026-10-15 00:54:30 +03:00
catarrh cf7ee77a9e v3.29.0: the batch kinds (delete / reinstall) + the delete-package pre-step
The batch plan is a list of named steps ({name, apdu, delete}) now and
_ram_install_run generalized into _ram_plan_run; POST /api/ram-batch/start
dispatches on kind:
- install-cap (default; delete_package prepends a cascade DELETE on the
  package AID - a leftover package blocks INSTALL [for load]),
- reinstall-app (_ram_reinstall_plan: DELETE the applet instance + INSTALL
  [for install] from the loaded package - no .cap, no LOAD),
- delete (_ram_delete_plan: one GP DELETE, aid + target app/package ->
  P2=00/P2=80).

A delete step tolerates 'not found' (6A88/6A82 - the object is already
gone, the goal is met); any other delete failure stops the chain with the
delete's reason.  The policy serves all three kinds and the single
/api/ram-install-app, which now deletes the instance first (make
selectable stays one step); /api/ram-install takes delete_package too.

The status/summary carry the run's kind (the panel labels follow:
installed/deleted/reinstalled) and each attempt carries the last three
steps (steps + steps_total) for the panel's per-step detail.  The PWA's
three batch entries are grouped at the end of the operation selector (an
optgroup); ramIsBatchOp() is the one source for "per-card batch".

Docs: help EN/RU, api.md, the repo AGENTS.  Tests: the plan builders per
kind, the validations, the delete-package prepend, the not-found
tolerance, the kind dispatch/status/notice, the steps tail, the endpoint
steps, the PWA wiring.  925 frontend + 943 Python; sw cache simple-v447.
2026-10-15 00:27:52 +03:00
catarrh 5ae8b1fdd1 v3.28.8: the batch panel's scroll preservation
The attempts box's content is replaced on every render (the batch panel is
static markup; the render fills the dynamic bits) and replacing a
scrollable element's content resets its scroll.  That was harmless while
the render ran once per attempt, but v3.28.7 made it run on every 2 s
poll, so scrolling back through the attempts during a run snapped to the
top each poll.

The render now captures the box's scrollTop before refilling it and
restores it afterwards.

Test: the stub box resets its scroll on an innerHTML write (like a
browser) and the render must keep the reader's position - verified to
fail without the restore.  920 frontend + 928 Python; sw cache
simple-v446.
2026-10-14 23:32:04 +03:00
catarrh 269e507580 v3.28.7: the batch panel freeze
ramBatchFetch's re-entrancy guard (_ramBatchFetching) is only cleared
when the fetch settles, and ramBatchApplyStatus returned without
rendering whenever it decided a fetch was due - so one stalled
/api/ram-batch/status request (the browser's fetch has no timeout) froze
the whole panel (counts, phase, notice, throbber) at the last successful
render while the server kept installing (live 2026-10-14: 31 installs,
the panel stuck at "Installed: 12"; the log showed all 31 attempts and a
healthy /api/status cadence, so the data was there).

- The summary render now runs on every poll before the fetch decision:
  the headline (phase, current card, counts, notice) always follows the
  2 s /api/status poll; the detailed fetch only adds the attempts table.
- A fetch guard older than RAM_BATCH_FETCH_STALE_MS (10 s) is released,
  so a stalled fetch cannot block the report forever - the next poll
  retries and the attempts table recovers.

Reproduced with the page's real functions: 12 good fetches, the 13th hung
-> the panel stuck at 12 (the reported number); with the fix it follows
the summary to 31 and the stale guard retries.  Tests: a hung fetch keeps
the counts following the summary; a stale guard is released and retried
(a fresh one is kept).  919 frontend + 928 Python; sw cache simple-v445.
2026-10-14 23:20:34 +03:00
catarrh 91996a941b v3.28.1: the batch panel polish
- A throbber (#ram-batch-spin) next to the phase line shows while a card
  is actually being installed (active && current - the stopping-mid-
  install phase included) and hides again while waiting.
- The waiting prompt is explicit now: "Insert next card. Waiting for a
  card..." (RU "Вставьте следующую карту. Ожидание карты…"); the old
  "Waiting for a card..." key is gone.
- The notice reports only the last *finished* card: starting the next
  card's install clears it (state['notice'] = '' alongside
  current/installing in _ram_batch_handle_card), so the previous card's
  verdict never lingers over the next install.  It stays visible while
  waiting - the "report the status + ask for the next card" behavior.
- The batch operation's RU label is "Пакетная установка (.cap на
  множество карт)"; the help row's title aligned.

Tests: the render test (the new prompt, the throbber visible while
installing / stopping-mid-install and hidden while waiting, an empty
notice hiding the box) and the worker test (the notice is empty
mid-install and set again for the new outcome).  915 frontend + 925
Python; sw cache simple-v439.
2026-10-14 18:04:22 +03:00
catarrh e8190c65f7 v3.28.0 review: the batch failure classification
- A non-ValueError exception from the install run (a card removed
  mid-chain, a transport error) was recorded as a 'skipped' attempt with
  the error text - but the chain had started and died, which is a
  failure.  Only a ValueError (a request/eligibility error: the preset's
  ISD keyset, the plan) is a skip now; anything else is a 'failed' record
  carrying its error.  The PWA's failed row falls back to that error when
  there is no failing step / status word to show.
- The panel's Clear button is disabled while a batch is active (the
  server refuses the clear with 409 anyway) - no pointless error alert.
- api.md and the repo AGENTS document the two outcomes.  Tests: a
  transport error mid-chain is a failure (and the card stays seen), the
  plan's input validation (the block size, a missing cap_hex), the failed
  row without a step, the shared ram_format field, and the Clear state.
  915 frontend + 921 Python; sw cache simple-v438 (unchanged).
2026-10-14 15:09:33 +03:00
catarrh ab373aff30 v3.28.0: the batch CAP install
The bulk workflow: one .cap and one set of install parameters applied to
card after card - swap cards and each one installs as soon as it is
equipped.  The server owns the loop, so closing the tab or reloading the
page does not interrupt it.

The install chain is now shared: _ram_install_plan(body) builds the
card-independent part (the parsed .cap, the INSTALL/LOAD sequence, the
block size, the transport knobs) and _ram_install_run(server, body, plan,
scc) runs one chain (the per-card preset/keyset/counter resolution
included); the /api/ram-install handler is the plan + run + response, and
the batch worker calls the same pair.  The plan is built once at Start,
so a bad .cap or parameter combination is a 400 there (a malformed
archive included - a zip parse error is the caller's, never a dropped
connection), not a per-card failure.  _op_keyset_resolve() is the module
form of the handler's _op_keyset() (the dead _keyset_guard method is
gone).

The worker (_ram_batch_worker, state _RAM_BATCH) polls the card state
every 0.5 s and acts on a new card session while connected; a card
already equipped at Start installs immediately.  Per card the preset is
matched by ICCID and its ISD role TAR supplies the keyset (v3.26.0) and
the counter seed (v3.20.0); a card with no readable ICCID, no matching
preset or no usable ISD keyset is recorded as skipped with its reason.
A card is attempted once per run (any outcome): re-equipping it records
"already processed in this batch (<outcome>)" and never installs again -
restarting the batch is the retry.  Failures carry the failing step and
the remote SW, are never retried and never stop the run; an unexpected
card-handling error is recorded and the loop keeps waiting.

Endpoints: POST /api/ram-batch/start (no card needed; 400/409), GET
/api/ram-batch/status (card-free - the panel polls it during an install),
POST /api/ram-batch/stop (the first _CARD_FREE_POST: it only sets a flag,
so the button works while the worker holds the card lock; a running
install always finishes first) and POST /api/ram-batch/clear (409 while
active).  /api/status carries the ram_batch summary (the detailed state
minus the attempts list), so the panel's headline - the live phase, the
current card, the counts - rides the 2 s poll; the attempts report (the
last 500 kept, the total counted) is fetched only when it changed.  The
batch and a test run exclude each other (409 both ways); the state is in
memory - a server restart ends a run.

PWA: the Batch install (.cap file, per card) RAM operation shares the
install form (ramInstallCapFields(); the single install uses it too and
now sends ram_format, and the block-size validation moved before the
modal), hides the preset selector + Execute and the preset-mismatch
banner, and renders Start/Stop/Clear, the phase headline (equip the next
card / installing card + live step / stopping after the current card),
the counts and the attempts table (newest first).  The panel only works
inside the visible SCP80 RAM pill.

Docs: help EN/RU, api.md (a section + the four table rows), the repo
AGENTS.  Tests: the state/report helpers, the per-card decisions, the
worker loop (a real thread + a fake install), the lifecycle and guards,
the HTTP endpoints (the malformed .cap, the stop under a held card lock,
the test-run refusal) and the PWA panel helpers/wiring.  914 frontend +
919 Python; sw cache simple-v438.
2026-10-14 15:01:56 +03:00