The card's contact pad sat mid-card; it now sits near the bottom edge,
away from the top-right cut corner (the cut side is the card's top).
No other geometry or animation changes.
1024 Python + 1063 frontend; simple-v473.
Redraw the inline card-into-reader SVG after sim.svg: a rounded card
with the top-right cut corner and the 3x2 contact pad, sliding behind
the reader bar. The viewBox grew to 72x48 and the resting position is
0, so the card is no longer clipped at the top; the insert slide is
20px. The currentColor/Tailwind theming and the reduced-motion
fallback are unchanged.
1024 Python + 1063 frontend; simple-v472.
A delete run whose DELETE answered 6A88/6A82 is still a success (the
object is gone, the goal is met) but it is no longer reported as a plain
"OK": the attempt result is 'already_absent', the counts gain that
bucket ("Deleted: N - Already absent: M"), the verdict reads "Already
absent" and the notice says the card was already absent. A reinstall's
tolerated pre-delete keeps the install outcome (the install is the
point).
1024 Python + 1063 frontend; simple-v471.
The waiting phase is the only moment the operator must act, so it now:
- shows a card-into-reader SVG animation in the status modal (inline
SVG, CSS keyframes in src/style.css; reduced-motion users get the
static card-above-the-slot picture);
- plays a short Web Audio chime once per card, edge-triggered in the
2 s poll so it never repeats (the AudioContext is unlocked by the
Start click);
- sets the tab title to "Insert next card" while the window is hidden;
- offers a speaker toggle next to the status (persisted as
simple_ram_batch_sound, on by default).
The prebuilt style.css was rebuilt: it had gone stale and lacked
utilities the UI already used (bg-blue-50 on the batch notice,
inline-flex, mx-2, max-h-48, w-44, min-w-24).
1022 Python + 1062 frontend green; simple-v470.
v3.34.0 was committed but never tagged; its changes ship in the v3.35.0
release, so the changelog names only released versions (the same rule
applied to 3.32.3 -> 3.32.4 and 3.32.6/3.32.7 -> 3.33.0).
The Read decoded pill used the same grey fill as a disabled control, so
an enabled pill looked unavailable (reported live). The inactive-but-
enabled state is now an outlined button (blue on white/slate) and the
grey fill plus dimming are reserved for the actually disabled state.
The active pill keeps a border of the same width so the row does not
shift.
Pinned in fs_edit.test.js. 1022 Python + 1058 frontend; simple-v469.
TS 31.102 4.2.27 scope: alpha; TP-DA and TS-SCA with their own length
conventions (DA = digits, SCA = octets, 0 = default SMSC) and a TON
select; TP-PID; TP-DCS with TS 23.038 hints; the relative TP-VP with the
TS 23.040 ladder; a per-parameter "absent (the ME default applies)"
state. Unchanged slots are copied verbatim (the live fixture's
DCS-absent 00 and present VP FF survive), alphanumeric TON-5 addresses
stay read-only, and every checked record is validated before any write.
Decoder fixes: the parameter-indicator bits were shifted (SCA on 5,
PID on 2, DCS on 3, VP on 4; the spec is DA/SCA/PID/DCS/VP on bits 1-5
with 6-8 reserved, 1 = absent), and the TP-DA length was read as octets
instead of digits (TS 23.040 9.1.2.5).
Live fixture: ffffffffffffffffffffffffffe9ffffffffffffffffffffffff07919733824009f0ffffffff0000ff
(SCA +79332804900, PID 00, DCS absent/00, VP FF) round-trips
byte-identically.
1022 Python + 1058 frontend green; simple-v468.
New client-side editors: the service tables EF.UST/IST/EST (a filterable
bit grid; unmodelled bits and the file length preserved), EF.ACC,
EF.PHASE, EF.HPPLMN, EF.AD (op-mode enum, additional info, MNC length
incl. 0 and the 3-byte SIM form), the PLMN-only lists
EF.PLMNsel/FPLMN/EHPLMN, EF.ICCID/EF.IMSI behind a confirmation, and the
cell-broadcast lists EF.CBMI/CBMID/CBMIR.
Framework: a bits form kind, a per-entry model(dec) hook, a per-entry
confirm prompt and hex fields.
Decoder fixes: EF.ACC class numbering (byte1 b8..b1 = 15..8, byte2 =
7..0), EF.AD operation mode (whole byte) and its MNC length rules,
EF.SST decoded with the SIM 2-bit allocated/activated layout (editing
stays disabled until that UI exists), and the EF.IMSI parity nibble
(9 odd / 1 even). A no-edit save of the new list files no longer
writes all-FF (the collected rows and the encoders disagreed on the
value shape).
Decoded editing for EF.DIR/EF.ARR/EF.SMS/EF.PBR is postponed by
decision (read-only viewing is enough); the survey records it.
1022 Python + 1033 frontend green; simple-v467.
Both versions were committed but never tagged; their changes ship in the
v3.33.0 release, so the changelog names only released versions (the same
rule applied to 3.32.3 -> 3.32.4).
The file manager gains a client-side decoded editor. EF_ENCODERS pairs
each editable EF with a pure encoder and a form schema (fields / list /
items); the file is read raw, the decoder fills the form, the browser
re-encodes it and the existing update path writes it.
Shapes: SPN (display-condition bits + Annex A text), PLMNwAcT/
OPLMNwAcT/HPLMNwAcT (rows with PLMN + ACT, add/remove),
ADN/FDN/SDN/MSISDN/LND/BDN (one form per record with a checkbox that
auto-checks on change; BCD, length and EXT handling; BDN's own X+15
layout) and PNN (per-record TLV items 43/45/80, add/remove, unknown
TLVs preserved).
Unchanged fields are copied verbatim (SSC/extended-BCD numbers, PLMN
rows including unmodelled satellite ACT bits, PNN codings/AddCI bits,
all-FF files, the BDN comparison-method byte), every checked record is
validated and encoded before anything is written, and edits that would
break a reference are refused.
Also: docs/decoded-editing.md (the spec survey behind the design and
the release waves), the efPlmnAct EC-GSM-IoT-only 0x88 fix, the PNN
spare-bit decode fix, and the /api/write|read cmd2-exception
false-success fix.
1022 Python + 1013 frontend green; simple-v466.
The decoded view is the client-side EF decoder's table over the raw
read's bytes. The collapsed "pySim JSON (server)" block was an
unrelated second decode - opening it ran another /api/read with mode
'decoded' (pySim's read_binary_decoded/read_records_decoded on the
card) - and it existed only when our decoder covered the file, so it
could not serve as a cross-check where one would matter.
/api/read keeps mode 'decoded' for API callers. Help, READMEs and the
project notes now state the table is built in the browser from the same
raw bytes; ef_decode.test.js pins the view's output and that no
mode 'decoded' read remains in the PWA.
1016 Python + 955 frontend green; simple-v465.
ramOpChanged() (v3.28.0) un-hid #ram-preset-warning by the operation
kind, but the banner's text and visibility belong to
spPresetWarningRender() (which already suppresses it for the batch
ops). With no warning text the empty amber box stayed visible until
the next status poll re-hid it - a flash on every RAM view / operation
switch. The visibility is content-driven again: the setHidden line is
gone and the renderer is called.
Pinned in frontend/tests/ram.test.js with the real renderer (the
pre-fix code fails the test). 1016 Python + 954 frontend green;
simple-v464.
The v3.32.3 changes (the traceback tail and the link-release wording)
were committed together with v3.32.4 and never tagged separately, so
the changelog named a version no release exists for. The entry is
merged into [3.32.4] and the published v3.32.4 release body matches it.
Live log: a card was pulled while the server started and the TERMINAL
PROFILE send raised CardConnectionException 0x80100016
(SCARD_E_NOT_TRANSACTED). The init's except caught it (no crash), but
the code then continued with the card object it had already built:
card_present and _CARD_CONNECTED became True - a phantom equipped card -
and the auto-equip's _CARD_CONNECTED early return then skipped every
later insertion, leaving only a manual Equip or a restart.
A PC/SC error in the startup init now prints one line (a traceback is
noise for a normal removal), clears the locals that feed those flags and
calls _clear_app_card_state(app); the presence monitor equips the next
card. Non-PC/SC errors keep their traceback.
Pinned in tests/test_startup_cardless.py (the branch and the flag
sources). 1016 Python + 953 frontend green; simple-v463.
v3.32.2 fixed the endless re-arm, but on Windows the first attempt
still failed on the old link (seen in the confirmation log: the
"self._con.connect()" traceback) and only the retry recovered - a failed
init, a 2-4 s delay and a traceback in the log.
The monitor's insert path now marks the transport stale under
_CARD_LOCK, so the first equip attempt rebuilds the PC/SC link. It also
covers a link created at a cardless startup (never connected). The
rebuild still happens in the equip worker, never in the pyscard monitor
thread, and _ensure_transport releases the old link first.
Docs: the READMEs' troubleshooting bullet.
Tests: an insertion marks the transport for the rebuild.
1014 Python + 953 frontend green; simple-v462.
After a card swap (or the first insertion after startup) on Windows the
auto-equip looped forever: "card inserted, initializing" / "card gone
during initialization", with no "retry 2/3" lines.
An equip with the card still present produced no card (app.card None),
and that branch reported "card gone" and returned True - a no-op success
- so the three retries never ran and the watchdog re-armed every ~5 s.
- The card-gone no-op is now only for a real removal (card_present
False); a card-present init that produced no card raises a failure.
- Any failed attempt marks the transport stale, so the next attempt
rebuilds the PC/SC link (a Windows swap can leave the old link unable
to connect; _ensure_transport releases the old link before building
the new one, so the 2026-09-24 stale-handle bug cannot recur).
- The error pySim swallowed (cmd2) is printed as "AUTO-EQUIP: init
output:" with its traceback - the failure is diagnosable from the log.
- Docs: the READMEs' troubleshooting bullet.
- Version strings + the PWA cache bumped together (simple-v460).
Tests: a card-present init without a card is a failure, a mid-init
removal stays a silent no-op, any failure marks the transport, the
stale-handle traceback reaches the log, and an end-to-end retry: a
failed init, the old link released, a fresh one built, the retry
succeeds. 1012 Python + 953 frontend green.
The import created presets with the store's default counter (1). Cards
whose counters are already past it had to be edited preset by preset
afterwards.
- The outfile preview form gains a "Start counter value" field (default
1, the store's value). The value is written into every created keyset
of every imported card - parse_outfile's `start_counter`, 1-10 hex
digits, whitespace stripped and uppercased; a bad value raises and the
endpoint answers 400. Without it the store's default still applies
(the field is omitted), so the API keeps its old behaviour.
- The preview shows the counter; an import stores the protocol's
10-digit form (counter_hex).
- Docs: api.md (the field), help EN/RU (the form).
- Version strings + the PWA cache bumped together (simple-v459).
Tests: the parser (every keyset, normalisation, unset, the bad values),
the endpoint (the preview's keysets, the 400, the stored padded form),
the PWA (the field's default, the controls' normalisation, both bodies).
1008 Python + 953 frontend green.
Reinstalling the package was slow because setup rebuilds pysim's
prerequisites (pyscard compiles from source on Linux), while a clone
updates with a plain `git pull`.
- tools/update.py (+ update.sh / update.bat in the package, and the
release workflow shipping them) updates a packaged install in place:
it downloads the latest release package, extracts it (zip-slip guard,
permission bits restored - zipfile drops them), replaces the app files
over the app directory and re-installs the app into the existing
.venv. The .venv and simple_lastrun.log are not in the package, so
pysim and its prerequisites survive: seconds, not a rebuild.
--check / --version / --zip (offline) / --no-pip / --force; the same
version is a no-op.
- CHANGELOG.md (Keep a Changelog, from the first packaged release;
earlier history is the git log). The release workflow now uses the
version's CHANGELOG section as the release body
(tools/changelog_section.py, falling back to generated notes).
- The READMEs document the update path and the `git clone --depth 1`
option for the clone path; setup prints the update hint.
- Version strings + the PWA cache bumped together (simple-v458).
Verified: 1007 Python + 953 frontend; the release-package test covers
the new files; the updater was exercised end to end (a v3.31.2 install
with a real venv updated to the 3.32.0 package: files replaced, the
venv marker kept, pip metadata 3.32.0, exec bits kept, a second run a
no-op, --check correct).