Conversation
…time Tauri embeds frontendDist into the compiled binary at build time (confirmed directly: editing the bundled dist/styles.css post-build and relaunching the same binary had zero effect on rendering), so any styling change previously needed a full rebuild to take effect -- painful with a Rust/Tauri build on constrained hardware, and heavy iteration on the stylesheet expected. New get_custom_css command (main.rs) reads a real file from the app's data directory (~/.local/share/FT8AF/styles.css on Linux) fresh on every launch, seeding it from the compiled-in default (public/styles.css, via include_str!) on first run so there's something to open and edit immediately. main.tsx now awaits this and injects the result as a <style> tag instead of a static Vite-bundled import, so editing that file and relaunching the app picks up changes with no rebuild at all -- same "resolved live, not baked in at compile time" idea already used for the Hamlib rig list. Verified end-to-end with a real screenshot test: launched the built binary, edited the on-disk styles.css to a loud, unmistakable color with no rebuild, relaunched the same binary, and confirmed the new color rendered. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…onflict
rig.rs's load_hamlib() does a bare dlopen("libhamlib.so.4") -- resolved
against whatever the dynamic linker finds first. On this machine that's the
distro-packaged libhamlib.so.4 (Hamlib 4.5.4), which has no QMX support at
all (confirmed: 0/283 rigs listed included QMX, and rig_init failed for
model 2057). A separately-built Hamlib 4.7.1 already exists at
/usr/local/lib/libhamlib.so.4 with QMX support (RIG_MODEL_QRPLABS_QMX
present) -- same soname as the system package, so whichever the linker
resolves first wins for any process that doesn't override the search path.
Not fixed by upgrading the system package: other software on this machine
(CQRLOG) depends on the distro Hamlib specifically. desktop/linux/run-ft8af.sh
instead sets LD_LIBRARY_PATH just for FT8AF's own process, so it
preferentially finds the newer build without touching anything system-wide.
Falls back to the system Hamlib (previous behavior) if no /usr/local build
exists.
Verified end-to-end through the wrapper: rig list jumps from 283 to 321
entries (QRPLabs QMX now included), and the app made a real, live CAT
connection to a physical QMX (kenwood_transaction traffic, frequency set,
54ms round-trip) -- confirmed via screenshot showing "rig: qmx" instead of
"no rig".
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…e widget background select and input elements set background: var(--panel) (dark), but WebKitGTK's native rendering doesn't fully respect that for <select> -- confirmed live, the rig/band dropdown and text fields render with a light background regardless, so the existing color: var(--text) (light, #d7e0e8 -- meant for dark panels) made every text box's contents nearly unreadable. Verified live via the new runtime styles.css: edited the on-disk file, relaunched with no rebuild, confirmed all Settings-tab inputs (Callsign, Grid, Display name, Radio) and both dropdowns (rig/band picker, Backend, Input/Output device) now render crisp black text. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Same root cause as the earlier unreadable-text fix (light --text color on a light native widget background), but a separate CSS property: caret-color was never set, so the blinking text cursor was effectively invisible. This explains a real, confusing user-facing symptom: with no visible caret, retyping a value without first clearing the field silently inserts new characters at the actual (but invisible) cursor position instead of replacing anything -- confirmed live, a garbled "AI5IIAI5IIAI555" ended up saved after a few real attempts to enter a callsign, matching exactly what duplicate-insertion-without-clearing produces. Root-caused by resetting the field to empty and typing a single character with the caret-color fix applied: cursor now renders as a clear, visible black line, confirmed both in a screenshot and by the user directly on the live display. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This reverts commit d42c4a8.
…ht native widget background" This reverts commit fb86646.
… field The earlier global select/input color override broke real keyboard input entirely (reverted in fb86646/28463d2 -- see project memory: project_ft8af_webkitgtk_input_bug.md). This is narrower and confirmed different: targets only the one Display name <input> via its placeholder text as a CSS attribute selector (no JSX change, no rebuild needed), not the shared select/input rule that also matches every native <select> dropdown. Verified live, twice, with a real keyboard: text renders white and readable, and typing into the field still works normally -- unlike every variant of the global rule change, which broke input every time it was tried. Callsign/Grid and the <select> dropdowns are untouched by this commit and still use native-default text color. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Extends the single-field-scoping approach validated in 9680d65 (Display name input) to every select that was unreadable against its native light background: Band, Rig control (Backend/Radio/Connection/Serial port/Baud), Audio (Input/Output device), and Developer waterfall FFT (Window function/FFT size/Averaging). Each gets its own id and a CSS rule targeting just that id -- the shared select/input rule stays untouched, since overriding color there is what broke real keyboard input earlier (see project_ft8af_webkitgtk_input_bug.md). Verified live with a real keyboard across two rebuild/retest cycles: closed-select display text and open dropdown-list text both render black and readable, Callsign/Grid typing still works normally, and none of the scoped selects show any of the input-blocking behavior the global rule change caused. Also commits main.rs's `--list-audio` debug helper (used earlier tonight to diagnose the audio device list, analogous to the existing --list-rigs) -- prints cpal's enumerated input/output devices and exits. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
New RX gain slider (0-100%, default 100%/unity) mirrors the existing TX gain pattern end to end: a lock-free atomic in the audio capture callback (AudioInput::set_gain(), read fresh per sample on the realtime thread, no locking), an EngineCommand::SetRxGain handler that persists to config and applies live without restarting capture, a set_rx_gain Tauri command, and an ipc.ts wrapper. Placement: next to the All/CQ/To me filter chips on the Decode screen, not buried in Settings -- real feedback was that this needs adjusting per-band (noise floor varies a lot band to band) while actively watching decodes, the same reasoning that will apply to relocating TX gain next to a future Tune button. Range settled at 0-100% (matching TX gain) after live testing: temporarily widened to 0-800% to confirm the control had real effect (confirmed: mute at 0%, a genuine clip warning near 800%, proportional movement of the raw dBFS meter in between), but adjustment past 100% wasn't practically useful -- the QMX's USB audio codec has no analog preamp stage (confirmed via amixer: only a capture on/off switch, no volume control), so there's no real gain headroom on the input side to exploit past unity. Settled on 0-100% for finer control resolution across the range that matters. Both TX and RX gain sliders now debounce their backend IPC call (~120ms after the last onChange) while keeping the displayed value instant -- dragging fires far more onChange events than needed, and that load stacking on the waterfall's own frequent canvas redraws correlates with a real WebKitGTK renderer crash observed live (the WebKitWebProcess child disappeared entirely, leaving a blank window, while the Rust backend kept running) -- not proven as the sole cause, but a safe mitigation regardless. Also clarified: the waterfall's lack of visible change across the gain range is by design (its own noise-floor-relative auto-leveling, stated in its own code comment), and the raw "audio dB" meter reading "silent" at low gain while decodes keep working is expected too -- it measures broadband RMS while the decoder/waterfall work in narrow FFT bins, and FT8 is designed to decode at very low SNR. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
# Conflicts: # desktop/src-tauri/src/engine.rs # desktop/src-tauri/src/main.rs # desktop/src/App.tsx
Select readability (styles.css): the per-id `color: #000` fixed the Linux
symptom but broke the other two platforms. WebKitGTK draws <select> as a
native GTK widget and ignores the author background, so --text sat on a
light control; WebView2 and WKWebView *do* honor the dark --panel
background, so black text there was black-on-#1a2129 -- and CI ships all
three. Opt selects out of native rendering with `appearance: none` so the
theme actually paints everywhere, redraw the arrow as a background image,
and pin both colors on the option list, which the platform draws outside
the page. Drops the dead #rig-radio-select selector along the way.
Stale on-disk stylesheet (main.rs): the seeded copy was frozen after the
first launch, so a later release that ships new markup would render it
unstyled with no in-app reset, and an interrupted first write left a
0-byte file that read back Ok("") and injected an empty <style> forever.
Stamp the seed with an FNV-1a hash of its own body: a file whose stamp
still matches is an untouched seed and is taken back when the compiled
default changes; an edited file, an unstamped one, or one we never wrote
is left alone. Empty or whitespace-only now re-seeds.
Unstyled fallback (main.tsx): with the bundled CSS import removed and
index.html linking nothing, the IPC read was the only source of styling
and the catch rendered anyway -- `npm run dev` in a plain browser, where
invoke rejects, came up as bare HTML. Fall back to the compiled-in copy.
RX gain: docs on SetRxGain, the engine field and set_gain still claimed
0.0-2.0 after the range was finalized at 0.0-1.0. Non-finite input now
falls back to the default instead of being clamped -- f32::clamp
propagates NaN, and the result is persisted where "NaN" parses back
cleanly, so one NaN would kill RX across restarts. The new full-scale
clamp in push_mono applies only above unity: at or below it, it could
only ever clip samples the device already delivered out of range, which
hard-clips the slightly-over-scale samples F32 backends (CoreAudio,
JACK/PipeWire) hand out on an otherwise-default install.
Linux launcher: LD_PRELOAD the one Hamlib rather than prepending
/usr/local/lib to LD_LIBRARY_PATH, which redirected every library FT8AF
resolves; and fall back to an installed ft8af on PATH so the script works
outside a source checkout, which is where its own docs send it.
Debounce: extracted to src/debounce.ts so it is testable, and cancelled
on unmount so a drag in flight cannot fire IPC after teardown.
Tests: resolve_styles/stamp round-trip and recovery cases, clamp_rx_gain
and the non-finite fallback for both gains, push_mono gain/clip/downmix
behavior, and the debounce module.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01D7Z6xhcHB3XspStFrQ5puU
Runtime-loaded styles.css; Linux Hamlib/QMX launcher wrapper; readable form-field text
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## staging #805 +/- ##
=============================================
+ Coverage 42.14% 42.64% +0.49%
+ Complexity 228 226 -2
=============================================
Files 270 267 -3
Lines 32257 32068 -189
Branches 3734 3664 -70
=============================================
+ Hits 13596 13675 +79
+ Misses 18385 18132 -253
+ Partials 276 261 -15
Flags with carried forward coverage won't be shown. Click here to find out more.
🚀 New features to boost your workflow:
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Promotion of
devintostaging. 12 commits, all in the desktop (Tauri) app — no Android changes in this batch.What's included
Runtime-loaded styles (PR #804)
styles.cssmoved todesktop/public/and loaded from disk at runtime instead of being bundled at build time, so theme tweaks don't need a rebuild.Live RX input gain
engine.rs/audio/input.rswith a debounced IPC call (debounce.ts+ unit tests).Text-visibility fixes
--textchange) were reverted in favor of the scoped version.Linux launcher
desktop/linux/run-ft8af.shwrapper that works around a real Hamlib/QMX version conflict.Debug helper
--list-audioCLI flag for enumerating audio devices.Commits
🤖 Generated with Claude Code
https://claude.ai/code/session_01D7Z6xhcHB3XspStFrQ5puU