Skip to content

feat(tui): prompt to power-cycle, show read/write progress, and fix the port box - #2

Merged
M0LTE merged 1 commit into
mainfrom
feat/tui-typing-and-progress
Aug 21, 2026
Merged

feat(tui): prompt to power-cycle, show read/write progress, and fix the port box#2
M0LTE merged 1 commit into
mainfrom
feat/tui-typing-and-progress

Conversation

@M0LTE

@M0LTE M0LTE commented Aug 21, 2026

Copy link
Copy Markdown
Owner

The three things from the bench, plus a diagnosis of the typing lag and a bug found on the way.

The power-cycle instruction is a prompt, not a log line

It was a line in the log, which is the wrong place for the one thing you have to go and do to the hardware. A read or a write now puts it on screen:

╭┤Reading the radio├───────────────────────────────────────╮
│                 POWER-CYCLE THE RADIO NOW                │
│                                                          │
│  Switch it off and back on. The radio latches            │
│  programming mode as it boots, so the tool has to be     │
│  listening before that happens - which it now is.        │
│                                                          │
│  Waiting up to 90 seconds. This box closes itself as     │
│  soon as the radio answers - no keystroke needed.        │
│                                             ⟦► Cancel ◄⟧ │
╰──────────────────────────────────────────────────────────╯

It dismisses itself when the radio answers, so the normal case needs no keystroke. Cancel or Esc abandons the operation rather than sitting through the rest of the 90-second wait. Anything that dismisses the box counts as cancelling, so Esc cannot leave a read running invisibly behind a closed prompt (it could, in the first cut of this).

Read and write show progress

writing 52% (88/168) next to the buttons, with a bar. The numbers come from the library rather than being guessed at: TaitProgrammer raises a ProgrammerProgress (phase, done, total) and takes a CancellationToken.

Cancelling a write is honoured only up to the point where the write block opens. Past that the radio's codeplug is being modified, and stopping half way would leave the block open and the codeplug partly applied, which is worse than finishing - so a started write always runs to its commit. Both halves of that are tested.

The bar is throttled to a few redraws a second. That matters more than it sounds: a write is over a thousand records, and for the reason below, an unthrottled bar would push a thousand full-screen repaints down an SSH link and become the slowest thing in the write.

The port box would not accept typing

DropDownList defaults to ReadOnly, which makes it a picker rather than the combo box it looks like. On a machine where the radio's port does not enumerate - which a plain USB-serial cable often does not - there was no way to name one, so interactive mode could not be used at all. The README has claimed since 0.6.0 that a port which did not enumerate can still be typed in. Now it can.

The typing lag: it is Terminal.Gui, and this app cannot fix it

Every character typed into a text box repaints the entire screen. Measured, median of three, on the real binary behind a pty that answers the driver's queries:

terminal cells per typed character
80x24 1,920 13,428 bytes
100x30 3,000 21,826 bytes
120x40 4,800 36,885 bytes
200x50 10,000 81,658 bytes

That is ~7-8 bytes per cell on screen, per keystroke, and it scales linearly with the window. On a maximised terminal over SSH, 82 KB a character is a second or two per keystroke. Caret moves and backspace in the same field cost ~135 bytes, which is why only insertion feels slow.

It is not this UI: a minimal Terminal.Gui app (one window, one text field, nothing else) does exactly the same. It is not a version away: 2.4.18-develop.31 behaves identically. Tried and rejected on the way to being sure:

  • the dialog's shadow, and the superview line canvas
  • the text field's autocomplete, and its suggestion generator
  • an in-window overlay instead of a modal dialog
  • a hand-written single-line field that marks only itself dirty - any redraw at all costs a full frame, so it bought nothing and lost selection and clipboard
  • the DOTNET driver instead of ANSI: cheaper (15 KB against 22 KB) but still a whole-screen repaint

The README now says so, with the three things that do help: a smaller terminal window while editing (80x24 costs a sixth of 200x50), tait-codeplug patch <port> ch0.rxfreq 144.812500 instead of the editor, or running the tool on the machine the radio is plugged into.

Testing

77 tests, up from 66. New: progress and cancellation against the scripted-radio double (including that a cancelled write never opens the block, and that a started one always commits), and the redraw throttle.

Verified end to end against a fake TM8100 served on a pty: the prompt appears and dismisses itself when the radio answers; Cancel and Esc both abandon cleanly and leave the app usable; a 265-record read and a 168-record write both complete with the bar moving and clearing afterwards.

Merging this wants a v0.7.0 tag; the CHANGELOG section is written.

🤖 Generated with Claude Code

https://claude.ai/code/session_01FkDFej82QnbjMJYFcwYyAZ

…he port box

Three things from the bench, plus what the typing lag turned out to be.

**The power-cycle instruction is a prompt now.** It was a line in the log, which
is the wrong place for the one thing the operator has to go and do to the
hardware. A read or a write puts it on the screen, and takes it down again by
itself the moment the radio answers - the normal case needs no keystroke at all.
Cancel, or Esc, abandons the operation instead of sitting through the remaining
90-second wait. Anything that dismisses the box counts as cancelling, so Esc
cannot leave a read running invisibly.

**Read and write show progress**, from the library rather than guessed at:
TaitProgrammer now raises a ProgrammerProgress with a phase, a count and a total
- sections for a read, records for a write - and takes a CancellationToken.

Cancelling a write is only honoured up to the point where the write block opens.
Past that the radio's codeplug is being modified and stopping half way would
leave the block open and the codeplug partly applied, which is worse than
finishing, so a started write always runs to its commit. Two tests hold both
halves of that.

Progress redraws are throttled: a write is over a thousand records, and with
Terminal.Gui repainting the whole screen for any change (see below), an
unthrottled bar would push a thousand full repaints down an SSH link and become
the slowest thing in the write.

**The port box would not accept typing.** DropDownList defaults to ReadOnly,
which makes it a picker rather than the combo box it looks like, so on a machine
where the radio's port does not enumerate there was no way to name one and the
interactive mode could not be used at all. The README has claimed since 0.6.0
that a port which did not enumerate can be typed in; now it can.

**On the typing lag itself**: it is Terminal.Gui, and it is not something this
app can fix. Every character typed into a text box repaints the entire screen,
measured at ~7-8 bytes per cell:

    80x24  ( 1920 cells)  13,428 bytes per typed character
    100x30 ( 3000 cells)  21,826
    120x40 ( 4800 cells)  36,885
    200x50 (10000 cells)  81,658

A minimal Terminal.Gui app - one window, one text field, nothing else - does the
same, so it is not this UI; 2.4.18-develop.31 does the same, so it is not a
version bump away. On a maximised terminal over SSH, 82 KB a character is the
second or two per keystroke that typing a frequency felt like. Caret moves and
backspace are ~135 bytes, which is why only insertion feels slow.

Tried and rejected: disabling the dialog's shadow, the superview line canvas,
the field's autocomplete and its suggestion generator; an in-window overlay
instead of a modal; a hand-written single-line field that only marks itself
dirty (any redraw at all costs a full frame, so it bought nothing and lost
selection and clipboard). The DOTNET driver is cheaper than the ANSI one (15 KB
against 22 KB) but still a whole-screen repaint.

Documented in the README with the three things that do help: a smaller terminal
while editing, `patch <port> ch0.rxfreq <MHz>` instead of the editor, or running
the tool on the machine the radio is plugged into.

Verified end to end against a fake TM8100 on a pty: the prompt appears, dismisses
itself when the radio answers, and Cancel and Esc both abandon the read cleanly;
a 265-record read and a 168-record write both complete with the bar moving and
clearing afterwards.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FkDFej82QnbjMJYFcwYyAZ
@M0LTE
M0LTE merged commit 24a05e1 into main Aug 21, 2026
1 check passed
@M0LTE
M0LTE deleted the feat/tui-typing-and-progress branch August 21, 2026 12:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant