Signed-off-by: Stan Grams <sjg@haxx.space>
16 KiB
UX Guidelines
This document captures the UI/UX design patterns, conventions, and principles observed across the trx-rs application. It covers the web frontend, CLI interfaces, configuration wizard, API design, and error handling.
Last reviewed: 2026-03-28
1. Web Frontend (trx-frontend-http)
1.1 Layout and Navigation
The web UI is a single-page application served from embedded assets (no build step). It uses a tab-based navigation model with six top-level tabs:
| Tab | Icon | Purpose |
|---|---|---|
| Main | House | Primary radio control: spectrum, frequency, mode, PTT, VFO, SDR controls |
| Bookmarks | Bookmark | Saved frequency/mode presets with folder organisation |
| Digital modes | Bar chart | FT8/FT4/FT2, WSPR, CW, APRS, AIS, VDES decode tables |
| Map | Pin | Leaflet map for APRS/AIS/FT8 station plotting |
| Settings | Wrench | Scheduler, background decode, history retention |
| About | Info circle | Server/client/radio/audio/decoder/integration details |
Tabs use inline SVG icons with a text label below. On narrow viewports the tab bar wraps and subtitles collapse to save space.
The Settings and About tabs each use a secondary sub-tab bar for further grouping (e.g. Settings > Scheduler | Background Decode | History).
1.2 Theming
The UI supports dark mode (default) and light mode toggled via a header button. Theme
preference persists in localStorage.
Additionally, nine colour styles are available via a dropdown:
- Original (default), Arctic, Lime, Contrast, Neon Disco, Donald (golden-rain), Amber, Fire, Phosphor
Each style provides a full CSS custom-property override set for both dark and light variants.
Styles are applied via data-style and data-theme attributes on <html>.
All colours reference CSS custom properties (--bg, --card-bg, --text, --accent-green,
--border-light, etc.) so components never use hard-coded colour values.
1.3 Typography
- Body:
system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif - Frequency display:
DSEG14 Classic(14-segment display font, loaded from CDN withpreload) - Labels: uppercase, 0.68-0.78 rem,
font-weight: 700,letter-spacing: 0.04em - Section labels use pill-shaped badges (
border-radius: 999px) with muted text
1.4 Responsive Design
Six breakpoints handle layout adaptation:
| Breakpoint | Behaviour |
|---|---|
> 1100px |
Full width with bookmark side gutters on spectrum |
< 1100px |
Side bookmark panels hidden |
< 900px |
Card fills viewport width, reduced padding |
< 760px |
Tab bar wraps, controls stack vertically, safe-area-inset padding for notched devices |
< 640px |
Bottom-fixed tab bar (mobile), subtitles hidden, compact header |
< 520px |
Further compact adjustments |
Touch-specific: @media (hover: none) and (pointer: coarse) enlarges hit targets.
The spectrum panel hints adapt: mouse users see "Scroll to zoom / Ctrl+Scroll to tune / Drag to pan" while touch users see "Pinch to zoom / Drag to pan".
1.5 Interactive Controls
- Jog wheel: Circular CSS-styled draggable dial for frequency tuning (skeuomorphic radial-gradient, grab cursor, shadow/inset). Plus/minus buttons flank it.
- Step unit buttons: Segmented button group (MHz / kHz / Hz) with
.activehighlight - Step scale: 1x / 0.1x multiplier toggle
- Frequency input: Monospace DSEG14 font, editable
<input>with disabled opacity fix - Mode selector:
<select>dropdown populated from rig capabilities - PTT / Power / Lock buttons: Three-column grid in the transmit/power section
- VFO picker: Button group (horizontal on desktop, vertical stack on mobile)
- WFM/SAM controls: Compact labelled controls (de-emphasis, audio mode, denoise, stereo pilot flag, CCI/ACI interference bars)
- SDR settings row: AGC checkbox, RF/LNA gain inputs with Set buttons, noise blanker
1.6 Spectrum and Waterfall
The spectrum panel uses <canvas> elements (WebGL renderer optional) and offers:
- Drag to pan, scroll to zoom, Ctrl+scroll to tune
- Bandwidth edges are draggable to resize the filter
- Keyboard shortcuts:
+/-zoom, arrows pan,0reset - Minimap for orientation when zoomed
- Resize grip to adjust spectrum height
- Controls: bandwidth input, auto-BW, sweet-spot, peak hold (0-60s), floor (dB), range (dB), auto-level, contrast gamma slider
- Waterfall/waveform split slider (20%-80%, default 50/50)
- Bookmark axis overlays on left/right sides at wider viewports
- Decoder overlays: RDS station name, AIS/VDES/FT8/APRS/CW bar overlays using
aria-live="polite"
1.7 Real-Time Data
- SSE (Server-Sent Events) on
/eventsfor rig state updates. Each SSE session gets a UUID, enabling per-tab rig selection without interfering with other tabs. - Named events:
data(state),session(session UUID),channels(virtual channels),b(spectrum bins as base64),rds,vchan_rds,ping(5-second heartbeat) - WebSocket on
/audiofor Opus-encoded RX audio streaming - Connection lost banner:
#server-lost-bannerwith pulsing dot, text "trx-server connection lost -- waiting for reconnect", usesaria-live="assertive" - Loading state: Centered "Initializing (rig)..." with subtitle, content hidden until ready
1.8 Accessibility
- All interactive elements have
aria-labelattributes - Spectrum overlays use
aria-live="polite"for screen reader announcements - Connection-lost banner uses
aria-live="assertive" aria-hidden="true"on decorative canvases and visual-only elements- SVG icons include
aria-hidden="true"with descriptive labels on parent buttons - Spectrum resize grip has both
titleandaria-label
1.9 Authentication UX
When auth is enabled, an auth gate blocks the UI with:
- Title: "Access Required"
- Subtitle: "Enter passphrase to continue"
- Password input + Login button (green accent, full-width)
- Optional "Continue as Guest" button (shown when RX passphrase is not set)
- Error message area (red
#ff6b6b) - Role badge display
Two roles: Rx (read-only) and Control (full access including TX/PTT).
Session cookie: trx_http_sid, HttpOnly, configurable Secure and SameSite attributes.
The header shows a Login/Logout button when auth is enabled (#header-auth-btn).
1.10 Multi-Rig Support
- Header rig switcher:
<select>dropdown in the top bar for switching between connected rigs - Per-tab rig binding: each SSE session independently selects a rig via
?remote=query parameter - Rig state isolation: only the disconnected rig shows the connection-lost banner
- About tab shows active rig, available rigs list
2. REST API Design
2.1 Conventions
- Read operations use
GET(e.g./status,/events,/decode/history,/rigs,/bookmarks) - Mutations use
POSTfor actions and toggles (e.g./set_freq,/toggle_power,/toggle_ft8_decode) - CRUD resources use proper verbs:
GET /bookmarks,POST /bookmarks,PUT /bookmarks/{id},DELETE /bookmarks/{id} - Batch operations:
POST /bookmarks/batch_delete,POST /bookmarks/batch_move - Nested resources:
/channels/{remote}/{channel_id}/subscribe,/scheduler/{remote}/status - Responses are JSON with
Content-Type: application/json - SSE stream uses
Content-Type: text/event-streamwithno-cacheandkeep-aliveheaders
2.2 Request Timeout
All rig command requests have a 15-second timeout (REQUEST_TIMEOUT). If the command
doesn't complete in time, the request returns an error rather than hanging.
2.3 Error Responses
401 Unauthorized:{"error": "Invalid credentials"}or{"error": "Authentication required"}429 Too Many Requests:{"error": "Too many login attempts, please try again later"}404 Not Found: Auth endpoints when auth is disabled500 Internal Server Error: Serialization failures- Rate limiting: 10 attempts per 60-second window per IP, counter resets on successful login
2.4 State Enrichment
API responses merge rig state with frontend metadata (FrontendMeta) via serde(flatten):
http_clients, rigctl_clients, audio_clients, rigctl_addr,
active_remote, remotes[], owner_callsign, owner_website_url,
owner_website_name, ais_vessel_url_base, show_sdr_gain_control,
initial_map_zoom, spectrum_coverage_margin_hz, spectrum_usable_span_ratio,
decode_history_retention_min, server_connected
This single-payload approach avoids extra round trips for UI configuration.
3. CLI Interface
3.1 Argument Style
Both trx-server and trx-client use clap for argument parsing with short and long flags:
-C, --config FILE Path to configuration file
--print-config Print example configuration and exit
-r, --rig NAME Rig backend name
-l, --listen ADDR Listen address
-p, --port NUM Port number
Positional arguments are used sparingly (e.g. RIG_ADDR for serial/TCP address).
3.2 Configuration Resolution
Config files are searched in priority order:
- Current directory:
trx-rs.toml - XDG config:
~/.config/trx-rs/trx-rs.toml - System:
/etc/trx-rs/trx-rs.toml
The loaded config path is logged: INFO Loaded configuration from /path/to/config.toml
3.3 Example Config Generation
--print-config outputs a complete, commented TOML file to stdout with example values
(callsign N0CALL, coordinates 52.2297, 21.0122). Each section has a header comment and
each field has an inline description.
3.4 Startup Log Sequence
Server:
INFO Loaded configuration from /path/to/config.toml
INFO Starting trx-server with N rig(s): [rig-names]
INFO Callsign: CALL
INFO [rig-id] Starting (rig: ft817, access: serial /dev/ttyUSB0 @ 9600 baud)
INFO Listening on 0.0.0.0:4530
Client:
INFO Loaded configuration from /path/to/config.toml
INFO Starting trx-client (remotes: [remote-names], frontends: http,rigctl)
INFO rigctl frontend for rig 'default' on 127.0.0.1:4532
4. Configuration Wizard (trx-configurator)
4.1 Interactive Mode
Uses the dialoguer crate for terminal prompts:
Selectmenus for enumerated choices (config type, rig model, access type, log level)Inputfor free-text with defaults (callsign defaults toN0CALL, listen defaults to127.0.0.1)Confirmfor yes/no questions (enable auth, set location, etc.)- Serial port auto-detection with fallback to
/dev/ttyUSB0
4.2 Non-Interactive Mode
--defaults generates a config file without prompts, using sensible defaults.
4.3 Config Validation
--check FILE validates an existing config file:
/path/to/config.toml: valid TOML
Detected type: server
warning: [general].log_level 'verbose' is invalid (expected: trace, debug, info, warn, error)
1 warning(s), 0 error(s)
Validates: TOML syntax, unknown keys, log levels, coordinate ranges (-90..90 lat, -180..180 lon with pair requirement), access types, port ranges (0-65535).
4.4 File Write Confirmation
Prompts before overwriting an existing file. Outputs Wrote /path/to/file on success.
5. Error Handling and User-Facing Messages
5.1 Error Message Conventions
- Contextual: Include file paths, section names, and peer addresses
"Failed to parse config file /path: error details""Unknown rig model: X (available: ft817, ft450d, soapysdr)"
- Actionable: Suggest alternatives when available
"Rig model not specified. Use --rig or set [rig].model in config.""Unknown frontend: X (available: http, rigctl, httpjson)"
- Structured: Use field=value format in structured logging
5.2 Log Level Guidelines
| Level | Usage |
|---|---|
INFO |
Startup milestones, configuration loaded, listening, client connect/disconnect, decoder state changes |
WARN |
Non-fatal issues: command took too long, panel lock blocking, VFO priming failed, initial tune failed |
ERROR |
Fatal or significant failures: CAT polling errors, client errors, parse failures |
Logs suppress module targets (with_target(false)) for cleaner output.
5.3 Connection State Communication
- Server logs:
"Client connected: {peer}","Client {peer} disconnected","Client {peer} closing due to shutdown" - Rig task:
"[rig-id] Rig backend ready","Serial: /dev/ttyUSB0 @ 9600 baud" - Web UI: Connection-lost banner with reconnect indication, per-rig isolation
5.4 Graceful Degradation
- Startup continues after non-fatal failures:
"Initial PowerOn failed (continuing)" - Stream errors are deduplicated with 60-second summaries to avoid log flooding
- Lock poisoning is recovered from rather than panicking
- Unknown SSE events or lagged broadcast channels are silently skipped
6. Branding and Customisation
6.1 Owner Branding
Configurable via TOML and exposed via FrontendMeta:
owner_callsign-- displayed in header subtitle and About tabowner_website_url/owner_website_name-- optional link in headerais_vessel_url_base-- base URL for linking AIS vessel MMSI numbers
6.2 UI Behaviour Configuration
http_show_sdr_gain_control-- show/hide RF gain controlshttp_initial_map_zoom-- default map zoom levelhttp_spectrum_coverage_margin_hz-- guard margin for spectrum center retunehttp_spectrum_usable_span_ratio-- fraction of spectrum span treated as usablehttp_decode_history_retention_min-- default history retention (per-rig overrides supported)
6.3 Embedded Assets
Logo and favicon are embedded at compile time via include_bytes!. The logo image has an
onerror handler to hide itself if loading fails (this.style.display='none').
7. Security UX
7.1 Route Access Classification
Routes are classified into three tiers:
| Tier | Examples | Requirement |
|---|---|---|
| Public | /, /index.html, /map, /auth/*, static assets |
None |
| Read | /status, /events, /audio, /decode, /spectrum, /bookmarks |
Rx or Control role |
| Control | /set_freq, /set_mode, /set_ptt, /toggle_power, all other POST |
Control role only |
7.2 Session Management
- Sessions are 128-bit random hex tokens stored in HttpOnly cookies
- Configurable TTL (default from TOML config)
- Expired sessions auto-pruned on access
- Constant-time passphrase comparison to mitigate timing attacks
7.3 TX Access Control
An additional tx_access_control_enabled flag can restrict transmit-related actions even
for Control-role users, providing an extra safety layer.
8. Virtual Channels (SDR)
Virtual channels allow SDR users to monitor multiple frequencies simultaneously:
- Channels appear in a picker row below the VFO controls
- CRUD API:
POST /channels/{remote}to create,DELETEto remove,PUTto update freq/mode/BW - Subscribe/unsubscribe audio per channel
- Background decode channels (hidden, no audio stream back)
- Channels auto-destroyed when out-of-bandwidth after center-frequency retune
- Channel-list changes broadcast to SSE clients via
event: channels
9. Design Principles (Inferred)
-
Server-rendered SPA: All HTML/CSS/JS embedded in the binary -- zero external build tooling, no CDN dependency for core functionality (CDN used only for fonts and Leaflet maps).
-
Progressive disclosure: Advanced controls (WFM, SAM, SDR settings, spectrum controls) are hidden by default and revealed based on the active mode and backend type.
-
Keyboard-first, touch-aware: Spectrum supports full keyboard navigation alongside mouse and touch gestures. Mobile breakpoints enlarge hit targets and adapt layout.
-
Real-time by default: SSE + WebSocket provide sub-second state updates without polling from the browser. 5-second ping heartbeat detects stale connections.
-
Per-tab isolation: Each browser tab gets its own SSE session UUID and can independently select a rig, preventing cross-tab interference.
-
Configuration over code: UI behaviour knobs (gain visibility, map zoom, history retention, spectrum margins) are exposed as TOML config rather than requiring code changes.
-
Graceful degradation: The UI handles server disconnection gracefully with visible banners, and only the affected rig shows as disconnected in multi-rig setups.
-
Defensive security defaults: Auth disabled by default for ease of setup, but when enabled, provides role-based access, rate limiting, constant-time comparison, and HttpOnly cookies.