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: "Sign in to continue"
- Username and password inputs + Login button (green accent, full-width)
- Error message area (red
#ff6b6b) - Role badge display
Two roles: User (read-only) and Admin (full access including user management).
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, login/session endpoints, static assets |
None |
| Read | /status, /events, /audio, /decode, /spectrum, /bookmarks |
User or Admin role |
| Control | /set_freq, /set_mode, /set_ptt, /toggle_power, all other POST |
Admin 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
- Passwords are verified against salted Argon2id hashes
7.3 User Management
Only administrators can list, add, update, or remove accounts. The final administrator cannot be removed or demoted.
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.