Commit Graph
77 Commits
Author SHA1 Message Date
sjgandClaude Opus 5 3a9bf1b7ce [fix](trx-frontend-http): drop the rig description from the top bar
CI / test (pull_request) Successful in 8m22s
CI / frontend (pull_request) Successful in 3m1s
CI / reuse (pull_request) Successful in 3s
CI / lint (pull_request) Successful in 2m17s
The header repeated the active rig's hardware string and mode list beside
the rig picker.  With a real SDR that reads

  SoapySDR driver=airspyhf,serial=c852eb5dd23539f8 · RX · SDR filters ·
  LSB · USB · CW · CWR · AM · +7 modes

which is longer than every other control in the bar combined, and it is
already on the About tab in full, split across its Rig, Active rig,
Connection, Modes and VFO rows.

Remove the element and the builder behind it.  Rig switching keeps its
feedback through the existing hint channel rather than by briefly
rewriting a permanent label, and the identity that belongs in a header —
the rig's display name — stays in the left subtitle.

The freed width is not spent: the tab strip now reaches Settings before
it needs to scroll, where it previously faded out during Statistics.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GdyUjuXejCEfiub675z6cz
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-08-02 18:59:28 +02:00
sjgandClaude Opus 5 dd5760c436 [style](trx-frontend-http): fade the scrolled tab strip edge
CI / lint (pull_request) Successful in 2m19s
CI / test (pull_request) Successful in 8m17s
CI / frontend (pull_request) Successful in 3m0s
CI / reuse (pull_request) Successful in 3s
CI / lint (push) Successful in 2m17s
CI / test (push) Successful in 7m33s
CI / frontend (push) Successful in 2m9s
CI / reuse (push) Successful in 3s
The page tabs scroll rather than wrap, so the last visible tab was sliced
mid-word ("Se…" for Settings), which reads as a rendering fault instead of
as an invitation to scroll.

Fade the trailing edge with a mask.  A colour-matched cover gradient is
the usual trick, but the card is transparent, so a cover would have to
track the page background across both themes and all nine styles; a mask
is colour-agnostic.  Only the trailing edge is faded, leaving the first
tab crisp while the strip sits at rest.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GdyUjuXejCEfiub675z6cz
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-08-02 18:48:34 +02:00
sjgandClaude Opus 5 eee3630f04 [feat](trx-frontend-http): compact single-row top bar
The header's height depended on the viewport width, and not even
monotonically: 112px at 1440, 169px at 1100, 131px at 900, 246px at 720.
Both control groups wrapped, so every width produced a different ragged
block — eight page tabs across four rows at 1100px, and action controls
across three.  Four different control heights (32, 34, 45 and 54px) sat
in the same row, the 54px one being the rig picker with its summary
stacked underneath, and on narrow viewports the icon buttons stretched to
fill half the row, rendering a play triangle centred in a 249px box.

Lay both groups out as one row that never wraps.  Controls are a uniform
2rem and no longer stretch, the rig summary sits inline beside its select,
and the page tabs scroll instead of wrapping.  Secondary controls —
layout, style and theme — move into an overflow menu when the bar cannot
hold them, leaving audio, record and the rig picker inline.

Deciding when they no longer fit needs natural widths, not rendered ones:
the nav has min-width 0 and scrolls, so it always shrinks to the leftover
space and always reports scrolling, and the bar reports overflow even when
nothing is clipped.  scrollWidth on the scroll container is its
unconstrained content width, which is what the fit test compares against
the space available.

Measured after the change: 72px at 1440, 1280, 1100, 900 and 480, every
control 32px, nothing clipped at any width.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GdyUjuXejCEfiub675z6cz
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-08-02 18:45:43 +02:00
sjgandClaude Opus 5 b31790ff48 [fix](trx-frontend-http): keep layout sections togglable
CI / lint (pull_request) Successful in 2m21s
CI / frontend (pull_request) Successful in 3m3s
CI / reuse (pull_request) Successful in 3s
CI / test (pull_request) Successful in 8m32s
CI / lint (push) Successful in 2m30s
CI / test (push) Successful in 8m36s
CI / frontend (push) Successful in 2m13s
CI / reuse (push) Successful in 4s
A layout seeds the collapsible sections; it should not hold them there.
applyLayout writes the disclosure state of the advanced, audio and
scheduler sections, and it runs far more often than a layout change:
render() calls applyRigList() for every SSE frame carrying `remotes`,
which calls setActiveRig() unconditionally, which re-applies the layout.

An operator who expanded a section that the selected layout collapses by
default therefore had it shut again within about a second, which read as
the section being locked by the layout — most visibly the scheduler under
Compact.

Write the section state only when the layout actually changes, or the
first time each section appears in the DOM, since the advanced controls
are constructed after the first applyLayout call.  Switching layout still
reseeds every section, so choosing a layout keeps meaning "give me these
defaults".

Verified in Chromium: with Compact selected, activating the scheduler
summary opens the section and it survives both a rig-state refresh and a
repeated applyLayout, while selecting Full still reseeds it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GdyUjuXejCEfiub675z6cz
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-08-02 17:46:56 +02:00
sjgandClaude Opus 5 0d4c657b97 [fix](trx-frontend-http): route feature bundles through the host contract
CI / lint (pull_request) Failing after 2s
CI / test (pull_request) Failing after 1s
CI / frontend (pull_request) Failing after 41s
CI / reuse (pull_request) Failing after 2s
CI / lint (push) Failing after 2s
CI / test (push) Failing after 2s
CI / frontend (push) Failing after 36s
CI / reuse (push) Failing after 1s
The bookmark fix addressed one instance of a defect the TypeScript
migration left across the feature entries.  app.js stopped being a
classic script, so its top-level declarations are no longer shared
globals, but the converted entries kept reading them as window
properties that nothing publishes.

Restore the broken behavior:

- ais, aprs, hf-aprs read serverLat, serverLon and haversineKm as
  undefined, so every positioned packet rendered an empty distance.
- ais, aprs, hf-aprs, cw, sat, vdes, wefax, wspr called an undefined
  postPath, so clear-history and decoder toggles threw.
- scheduler read authRole as undefined, so the lazy-load path never
  self-initialized and the Settings tab opened an inert scheduler.
- background-decode read authEnabled as undefined, so control gating
  fell back to role-only.
- vchan read fifteen application values and services as undefined:
  mode and bandwidth sync, the out-of-band hint, RX audio restart, and
  the frequency field all silently no-opped on a virtual channel.
- vchan wrapped window.refreshFreqDisplay, capturing an undefined
  original exactly as it did for setRigFrequency, so leaving a channel
  never restored the application's own frequency display.
- _audioChannelOverride was a const that nothing could assign, so RX
  audio always subscribed to the primary channel.
- ftx-family read fmtTime, a helper legacy ft8.js owned locally, so
  decode bar timestamps rendered empty.

Declare the contract once in plugins/host.ts and import it from the
feature entries, rather than restoring globals that
docs/frontend-architecture.md excludes.  trx.state gains jogUnit,
rxActive and audioChannelOverride, and makes lastModeName writable;
trx.core gains the tuning, RDS, WFM, jog and RX audio services the
entries need.  vchan interception moves to an interceptFreqDisplay
service method that refreshFreqDisplay calls, matching the frequency,
mode and bandwidth interception it already registers.

Reading registry-built elements through a strict lookup is the same
defect as in bookmarks: renderTimelineNeedle guards its result, but
schedulerEl throws, so the now-initializing scheduler crashed on the
timeline needle group that its own SVG creates.

Feature tests move onto a shared host fixture, and entries that now
import a common module are bundled through bundleEntry like the other
shared-module entries.  Covers scheduler self-initialization and the
distance path that the bare window reads broke.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GdyUjuXejCEfiub675z6cz
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-08-02 11:20:17 +02:00
sjgandClaude Opus 5 23dbcac5b6 [fix](trx-frontend-http): restore bookmark host contract
CI / lint (pull_request) Failing after 1s
CI / test (pull_request) Failing after 1s
CI / frontend (pull_request) Failing after 42s
CI / reuse (pull_request) Failing after 0s
The TypeScript migration turned app.js from a classic script into an ES
module, so its top-level declarations stopped being shared globals.
bookmarks.ts was converted verbatim and kept reading them as window
properties, which app.ts no longer publishes.

Every bookmark interaction read undefined: the Add Bookmark and Select
All buttons stayed hidden because the auth check saw no authEnabled or
authRole, per-rig scopes were missing from the scope picker and the move
target, decoder checkboxes were never built, and Tune threw on
bridge.postPath before issuing a single request.

Extend the typed window.trx host contract instead of restoring globals,
as docs/frontend-architecture.md closes the standalone window property
list.  trx.state publishes authEnabled; trx.core publishes
setRigFrequency, applyLocalTunedFrequency, armOptimisticFrequency,
syncBandwidthInput, scheduleSpectrumDraw, and onDecoderRegistryReady.

Replace the vchan setRigFrequency wrapper with an interceptFrequency
service method, matching interceptMode and interceptBandwidth.  The
wrapper captured an undefined original and silently dropped every tune;
routing interception through setRigFrequency also restores virtual
channel redirection for the application's own tuning.

Read registry-built elements through bmOptionalEl, since bmEl throws and
the decoder checkboxes and decode toggle buttons are legitimately absent
until the registry arrives.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GdyUjuXejCEfiub675z6cz
Signed-off-by: Stan Grams <sjg@haxx.space>
2026-08-02 10:15:56 +02:00
sjg 695434942f chore: complete TypeScript migration cleanup
CI / lint (pull_request) Failing after 2s
CI / test (pull_request) Failing after 2s
CI / frontend (pull_request) Failing after 42s
CI / reuse (pull_request) Failing after 2s
CI / lint (push) Failing after 2s
CI / test (push) Failing after 1s
CI / frontend (push) Failing after 44s
CI / reuse (push) Failing after 2s
2026-08-01 22:39:37 +02:00
sjg a1a8d1d1d3 test: cover lazy map and rig startup flows 2026-08-01 22:34:22 +02:00
sjg ddb33b6ff3 feat: generate frontend status metadata contract 2026-08-01 22:27:35 +02:00
sjg f87289b129 fix: validate typed frontend startup in Chromium 2026-08-01 22:25:10 +02:00
sjg 812359c744 refactor: enforce typed frontend runtime boundaries 2026-08-01 22:19:50 +02:00
sjg d929ad3bde build: consolidate typed frontend module graph 2026-08-01 21:54:00 +02:00
sjg ddd151c1ee refactor: convert main frontend application to TypeScript 2026-08-01 19:18:48 +02:00
sjg 5136704826 refactor: extract typed spectrum math 2026-08-01 18:19:02 +02:00
sjg e300cc343d refactor: extract typed auto bandwidth estimator 2026-08-01 17:59:04 +02:00
sjg 1fb196a64e refactor: extract typed navigation routes 2026-08-01 17:18:41 +02:00
sjg c8cc235e88 refactor: extract strict CBOR decoder 2026-08-01 17:04:13 +02:00
sjg ab3fe4bbb7 refactor: extract typed frontend formatters 2026-08-01 16:42:54 +02:00
sjg 40accb9697 refactor: extract typed authentication API 2026-08-01 16:39:52 +02:00
sjg cbca810a30 refactor: extract typed decoder registry 2026-08-01 16:28:39 +02:00
sjg 26a167cb82 refactor: replace feature globals with typed services 2026-08-01 16:05:40 +02:00
sjg 26be675242 build: enable frontend module code splitting 2026-08-01 14:40:59 +02:00
sjg 844de809a4 fix: preserve bandplan override lookup 2026-08-01 14:23:29 +02:00
sjg e910e644d5 refactor: extract typed frontend core utilities 2026-08-01 13:48:51 +02:00
sjg 50e07e2715 refactor: add typed frontend bootstrap 2026-08-01 13:43:46 +02:00
sjg 82121491c5 refactor: convert map feature to strict TypeScript 2026-08-01 13:39:41 +02:00
sjg 0338c8b8d2 refactor: convert scheduler to typed module 2026-08-01 13:23:30 +02:00
sjg c7994347e9 refactor: convert bookmarks to typed module 2026-08-01 13:13:52 +02:00
sjg 9fef0ebc7b refactor: route all decoders through plugin runtime 2026-08-01 13:06:38 +02:00
sjg 508cf2ba87 refactor: add typed decoder plugin runtime 2026-08-01 13:02:54 +02:00
sjg 6a83e2e90a refactor: convert virtual channels to TypeScript 2026-08-01 12:57:09 +02:00
sjg fc2f55bab9 refactor: convert satellite scheduler to TypeScript 2026-08-01 12:54:03 +02:00
sjg 4fb65971e4 refactor: convert satellite view to TypeScript 2026-08-01 12:51:22 +02:00
sjg 021d31d780 refactor: share typed APRS plugin contracts 2026-08-01 12:48:33 +02:00
sjg 074c67c7b9 refactor: convert APRS plugin to TypeScript 2026-08-01 12:43:40 +02:00
sjg d93f784aad refactor: convert AIS plugin to TypeScript 2026-08-01 12:39:20 +02:00
sjg ec59908e0d refactor: convert background decode controls to TypeScript 2026-08-01 12:36:27 +02:00
sjg 7251ec276d refactor: convert WEFAX plugin to TypeScript 2026-08-01 12:33:38 +02:00
sjg 0ab0f80986 refactor: convert VDES plugin to TypeScript 2026-08-01 12:30:17 +02:00
sjg 2cbcc23fec refactor: move plugin loading into typed registry 2026-08-01 12:26:29 +02:00
sjg 968fb3ea2d refactor: type the local Leaflet AIS adapter 2026-08-01 12:23:37 +02:00
sjg 5ba3ecf59b refactor: make FT8 family a typed module 2026-08-01 12:21:13 +02:00
sjg 03bdc10b02 refactor: convert CW plugin to TypeScript 2026-08-01 12:18:51 +02:00
sjg d96624be4f refactor: convert WSPR plugin to TypeScript 2026-08-01 12:15:19 +02:00
sjg a5dccd5489 refactor: consolidate FT2 and FT4 plugins in TypeScript 2026-08-01 12:12:27 +02:00
sjg e6f593b959 refactor: convert screenshot support to TypeScript 2026-08-01 12:08:44 +02:00
sjg 9ac6d7f82c refactor: convert WebGL renderer to TypeScript 2026-08-01 12:07:02 +02:00
sjg a58553ba66 refactor: convert decode history worker to TypeScript 2026-08-01 12:03:19 +02:00
sjg 7442437757 refactor: convert shared UI core to TypeScript 2026-08-01 12:01:07 +02:00
sjg bbc53d56b0 feat: generate typed frontend API contracts 2026-08-01 11:58:09 +02:00