Closes#52. The issue had no body, so I rendered the panel first and worked from what was actually wrong with it.
What was wrong
It listed the same bookmarks twice. A status card on top named the selected ones and what each was doing; a checklist below named all of them with the checkboxes. Choosing what to decode and reading what was decoding were two passes over the same names, with nothing tying a row in one list to its twin in the other.
The checklist was shoved against the right edge. Its row sets align-items: flex-end, and an inline flex-direction: column on the same element turns that into "push every child right". The filter box spanned the panel; the list and the bulk buttons hugged the far edge.
Two races left it not merely ugly but useless:
The rig was whatever the panel was handed at load. Loading before the rig list arrived handed it null, and the next telling only came when the operator switched rigs — so the panel sat empty and silent. Reproduced in the fixture: nothing appeared until setRig was called by hand.
The settings panels are wired once the session is up, but their modules import asynchronously and the wiring skipped whichever had not arrived. A panel that missed it kept dead buttons for the rest of the session — no filter, no Select All, no Save. This is why Save sent no request at all on the first run of the new test.
Smaller things: Enable hidden background decoder channels sitting under a label already reading Background decode; states rendered ✓ Active / △ Out of span beside a dot that already carried the state; one bare "No background decode bookmarks configured." for every kind of empty; and a Save button live from the moment the panel opened with nothing to save.
What it is now
One list. A row carries its checkbox, name, frequency, mode, decoder and its own live state, so picking and watching happen in the same place. What the rig can hear sits beside the switch — it is the reason a selected bookmark reads Out of span — and the selection adds itself up under the list ("3 bookmarks selected, 1 decoding now."). States say what they mean, with the reason on hover: Out of span, Scheduler has it, Nobody listening, Bookmark gone. The empty list distinguishes "no bookmarks yet" from "none of your bookmarks name a decoder that runs in the background", and says what to do about it. Save offers itself only when something has changed.
Rows stack to two lines under 600px rather than pushing the frequency off the edge.
Verification
New tests/background-decode.mjs in the browser suite: the panel fills itself with no rig switch and no reload; rows carry both the saved selection and the per-row state; the span and summary lines read correctly; Save goes inert → live after a change → inert after saving, with the change surviving a reload; and the empty panel says what to do.
The fixture gained the /background-decode/… routes and a realistic background_decode flag per decoder. It had them all false, so the panel had nothing to offer and any test of it would have passed vacuously.
Full browser suite, 59 unit tests, typecheck and lint all pass.
The rig-handover fix is in applyRigList, so the Scheduler panel gets it too — it had the same latent race.
Closes #52. The issue had no body, so I rendered the panel first and worked from what was actually wrong with it.
## What was wrong
**It listed the same bookmarks twice.** A status card on top named the selected ones and what each was doing; a checklist below named all of them with the checkboxes. Choosing what to decode and reading what was decoding were two passes over the same names, with nothing tying a row in one list to its twin in the other.
**The checklist was shoved against the right edge.** Its row sets `align-items: flex-end`, and an inline `flex-direction: column` on the same element turns that into "push every child right". The filter box spanned the panel; the list and the bulk buttons hugged the far edge.
**Two races left it not merely ugly but useless:**
- The rig was whatever the panel was handed at load. Loading before the rig list arrived handed it `null`, and the next telling only came when the operator switched rigs — so the panel sat empty and silent. Reproduced in the fixture: nothing appeared until `setRig` was called by hand.
- The settings panels are wired once the session is up, but their modules import asynchronously and the wiring skipped whichever had not arrived. A panel that missed it kept **dead buttons for the rest of the session** — no filter, no Select All, no Save. This is why Save sent no request at all on the first run of the new test.
Smaller things: `Enable hidden background decoder channels` sitting under a label already reading `Background decode`; states rendered `✓ Active` / `△ Out of span` beside a dot that already carried the state; one bare "No background decode bookmarks configured." for every kind of empty; and a Save button live from the moment the panel opened with nothing to save.
## What it is now
One list. A row carries its checkbox, name, frequency, mode, decoder and its own live state, so picking and watching happen in the same place. What the rig can hear sits beside the switch — it is the reason a selected bookmark reads *Out of span* — and the selection adds itself up under the list ("3 bookmarks selected, 1 decoding now."). States say what they mean, with the reason on hover: *Out of span*, *Scheduler has it*, *Nobody listening*, *Bookmark gone*. The empty list distinguishes "no bookmarks yet" from "none of your bookmarks name a decoder that runs in the background", and says what to do about it. Save offers itself only when something has changed.
Rows stack to two lines under 600px rather than pushing the frequency off the edge.
## Verification
- New `tests/background-decode.mjs` in the browser suite: the panel fills itself with no rig switch and no reload; rows carry both the saved selection and the per-row state; the span and summary lines read correctly; Save goes inert → live after a change → inert after saving, with the change surviving a reload; and the empty panel says what to do.
- The fixture gained the `/background-decode/…` routes and a realistic `background_decode` flag per decoder. It had them all `false`, so the panel had nothing to offer and any test of it would have passed vacuously.
- Full browser suite, 59 unit tests, typecheck and lint all pass.
The rig-handover fix is in `applyRigList`, so the Scheduler panel gets it too — it had the same latent race.
The panel listed the same bookmarks twice: a status card at the top naming
the selected ones and what each was doing, and a checklist below naming all
of them with the checkboxes. Neither list said which row in the other it
meant, so choosing what to decode and reading what was being decoded were two
passes over the same names. The checklist itself sat squeezed against the
right-hand edge, because the row it lives in sets `align-items: flex-end` and
an inline `flex-direction: column` turned that into "push everything right".
It is one list now. A row carries its checkbox, its name, its frequency,
mode and decoder, and its own state, so picking and watching happen in the
same place. What the rig can hear moves up beside the switch, where it
explains why a selected bookmark reads out of span, and the selection adds
itself up under the list. The states lose the ✓/△/✗ they carried next to a
dot that already said as much, and say what they mean: "Out of span",
"Scheduler has it", "Nobody listening", each with the reason on hover. The
empty list now says which of the two reasons it is empty for, and what to do.
Save was live from the moment the panel opened, with nothing to save; it now
offers itself only when something has changed.
Two races behind it, both of which left the panel useless rather than ugly:
* The rig was whatever the panel was handed at load. Loading before the rig
list arrived handed it null, and the next telling only came when the
operator switched rigs — so the panel stayed empty and silent. Every rig
list refresh now passes the rig on, and both panels ignore one they have.
* The settings panels are wired once the session is up, but their modules
import asynchronously and the wiring skipped whichever had not arrived.
A panel that missed it kept dead buttons for the rest of the session: no
filter, no Select All, no Save. Wiring runs again when the modules land.
Closes#52
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SyX26FCpMQxiBoC7r5K1A7
Signed-off-by: Stan Grams <sjg@haxx.space>
sjg
merged commit a19633e81f into main2026-08-07 19:54:36 +02:00
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Closes #52. The issue had no body, so I rendered the panel first and worked from what was actually wrong with it.
What was wrong
It listed the same bookmarks twice. A status card on top named the selected ones and what each was doing; a checklist below named all of them with the checkboxes. Choosing what to decode and reading what was decoding were two passes over the same names, with nothing tying a row in one list to its twin in the other.
The checklist was shoved against the right edge. Its row sets
align-items: flex-end, and an inlineflex-direction: columnon the same element turns that into "push every child right". The filter box spanned the panel; the list and the bulk buttons hugged the far edge.Two races left it not merely ugly but useless:
null, and the next telling only came when the operator switched rigs — so the panel sat empty and silent. Reproduced in the fixture: nothing appeared untilsetRigwas called by hand.Smaller things:
Enable hidden background decoder channelssitting under a label already readingBackground decode; states rendered✓ Active/△ Out of spanbeside a dot that already carried the state; one bare "No background decode bookmarks configured." for every kind of empty; and a Save button live from the moment the panel opened with nothing to save.What it is now
One list. A row carries its checkbox, name, frequency, mode, decoder and its own live state, so picking and watching happen in the same place. What the rig can hear sits beside the switch — it is the reason a selected bookmark reads Out of span — and the selection adds itself up under the list ("3 bookmarks selected, 1 decoding now."). States say what they mean, with the reason on hover: Out of span, Scheduler has it, Nobody listening, Bookmark gone. The empty list distinguishes "no bookmarks yet" from "none of your bookmarks name a decoder that runs in the background", and says what to do about it. Save offers itself only when something has changed.
Rows stack to two lines under 600px rather than pushing the frequency off the edge.
Verification
tests/background-decode.mjsin the browser suite: the panel fills itself with no rig switch and no reload; rows carry both the saved selection and the per-row state; the span and summary lines read correctly; Save goes inert → live after a change → inert after saving, with the change surviving a reload; and the empty panel says what to do./background-decode/…routes and a realisticbackground_decodeflag per decoder. It had them allfalse, so the panel had nothing to offer and any test of it would have passed vacuously.The rig-handover fix is in
applyRigList, so the Scheduler panel gets it too — it had the same latent race.The panel listed the same bookmarks twice: a status card at the top naming the selected ones and what each was doing, and a checklist below naming all of them with the checkboxes. Neither list said which row in the other it meant, so choosing what to decode and reading what was being decoded were two passes over the same names. The checklist itself sat squeezed against the right-hand edge, because the row it lives in sets `align-items: flex-end` and an inline `flex-direction: column` turned that into "push everything right". It is one list now. A row carries its checkbox, its name, its frequency, mode and decoder, and its own state, so picking and watching happen in the same place. What the rig can hear moves up beside the switch, where it explains why a selected bookmark reads out of span, and the selection adds itself up under the list. The states lose the ✓/△/✗ they carried next to a dot that already said as much, and say what they mean: "Out of span", "Scheduler has it", "Nobody listening", each with the reason on hover. The empty list now says which of the two reasons it is empty for, and what to do. Save was live from the moment the panel opened, with nothing to save; it now offers itself only when something has changed. Two races behind it, both of which left the panel useless rather than ugly: * The rig was whatever the panel was handed at load. Loading before the rig list arrived handed it null, and the next telling only came when the operator switched rigs — so the panel stayed empty and silent. Every rig list refresh now passes the rig on, and both panels ignore one they have. * The settings panels are wired once the session is up, but their modules import asynchronously and the wiring skipped whichever had not arrived. A panel that missed it kept dead buttons for the rest of the session: no filter, no Select All, no Save. Wiring runs again when the modules land. Closes #52 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SyX26FCpMQxiBoC7r5K1A7 Signed-off-by: Stan Grams <sjg@haxx.space>