A night's scan leaves hundreds of files, most worth nothing and a few of
them the reason it was left running. Sorting that out meant leaving the
browser and going at the directory with mv and rm.
Five keys, meant to be pressed once each going down the list:
S I N file it into saved/, investigate/ or noise/
u put the last one filed back
d delete it and its sidecars, for good -- asks first
m lock the frequency out, so no later scan stops on it
Each of these acts on the whole capture -- the .wav, the JSON sidecar, the
IQ, the transcript and the decoded data -- because a recording in one
directory and its transcript in another is a pair nothing will ever put
back together. A move that cannot be finished puts back whatever already
moved. The cursor stays on the row it was on, which is now the next
recording, since a cursor that jumped would make one-key-per-recording
impossible.
m writes to the lock-out list in the settings file, the same one the
scanner's own l key maintains, so a birdie found while reading last night's
recordings is gone from tonight's. It says "the next scan": one already
running read its settings when it started.
The subdirectories sit under the recordings directory, so a scan writing
there never looks in them, and saunterbrowse ~/bandsaunter/saved reads one
back.
Also here, because this is the first part of the browser that writes:
- The help screen is back inside eighty by twenty-four. It had grown past
the bottom of an ordinary window, which puts "q quit" off the screen.
- The footer drops keys in a deliberate order when the window is narrow,
rather than ellipsising whichever happened to be at the end.
- Moving or deleting what is playing stops the player first.
- The pty harness accepted an env and ignored it, so a test aimed at a
throwaway settings directory wrote to the real one. It honours it now,
and conftest redirects the settings directory for every test besides.
1014 tests.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016PsWPTweCT6pwxKngvVxcg
Resizing the terminal during a scan left the screen full of wreckage:
box corners in the middle of a line, borders twice the width of the
window, a "receiver" header printed eight times down the left edge. Four
separate defects, which is why it looked so bad.
Live rendering works by moving the cursor back over the frame it drew
last time and overwriting it. That is only correct while the frame is
still where it was put, and none of these programs noticed when it was
not.
1. Nothing detected a resize. Both the scan display and saunterbrowse
now compare the console size on every frame and clear the screen when
it changes -- polled rather than handled as a signal, because the
display is redrawn several times a second anyway and a signal handler
that runs in the middle of a write has to be right about far more than
this does. Anything printed before the scan started scrolls away at
that point, which the manual now says.
2. The layout's model of its own height was wrong, in two places that
cancelled. The sweep panel was counted as one line shorter than it
is, the hit list as one line taller. The sum came out right whenever
both were drawn and wrong on a terminal too short for the hit list --
where the frame then overflowed by one line on every refresh and the
top of it marched down the screen. That is what the eight headers
were. Each panel height is a named constant now, and a test checks
every one of them against what is actually rendered.
3. Lines inside the panels could wrap. A band name, a long status line
or a decoded message made a panel a row taller than the arithmetic
allowed for, with the same result. Every one is drawn on a single
line and ellipsised now. The receiver panel drops its optional parts
instead, keeping the tuner and the flags: "SIMULATED" disappearing off
the end of a narrow line is how somebody comes to believe they are
listening to the air.
4. saunterbrowse's full-screen views did not fill the screen. Nothing
erases the alternate screen between frames -- the cursor is sent home
and the new frame written over the old one -- so pressing t or ? on a
tall window left most of the recording list visible underneath. Both
are wrapped in a layout now, which fills the terminal exactly.
The layout also gives up the receiver panel on a very short terminal,
which it previously had no way to do: on eight rows the smallest frame it
could describe was nine lines.
Testing this by rendering to a wide Console and reading the text back
cannot work -- whether the cursor lands where it should is a property of
the terminal, not of the renderable. So tests/terminal.py runs the
program in a pty, resizes the window underneath it the way a window
manager does, and feeds what it writes to a terminal emulator whose
screen is then read. Every fix above has a test that fails without it,
checked by reverting each one in turn. pyte is a dev dependency and
those tests skip without it; the arithmetic ones need nothing.
Also: t now opens the reader for a capture that carries decoded data
rather than speech, because the decoded panel already told the reader to
press it.
869 -> 949 tests.