Repository navigation
Conversation
VOX TX crashed the app (SIGABRT) every time it should have keyed, once a DigiRig/USB audio interface was attached. Attaching the interface posts an AVAudioEngineConfigurationChange, which tears down every connection in the shared AVAudioEngine — including the TX playerNode -> mainMixerNode link. TxPlayerService cached `isAttached = true` for the app's lifetime and never reconnected, so at TX time AVAudioPlayerNode.play() ran on a disconnected node and AVFAudio raised an uncaught NSException -> abort. AudioCaptureService only rebuilds the capture (input) side on config change, so the TX side was left dead. Split the lifecycle into attach (once) vs connect (per graph rebuild) via a pure `TxConnectionState`, observe AVAudioEngineConfigurationChange in TxPlayerService, and invalidate the connection + drop the stale converter so the next play() reconnects and rebuilds the converter against the current mixer format. Diagnosed from three identical device crash reports (TxPlayerService.play -> AVAudioPlayerNode play -> StartImpl NSException). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Follow-up to the config-change reconnect fix, resolving three review findings: 1. Residual crash window (correctness): a config change delivered between ensureConnected() and playerNode.play() still tore down the node connection, and play() on a disconnected node raises an uncaught Obj-C NSException that aborts the app — Swift's `try` can't catch it. Added a minimal Obj-C exception-catch shim (ExceptionCatcher + bridging header) and route the stop/schedule/play keying through it; on a raised exception we invalidate the connection and bail (TX lost for that slot, app survives). Also made ensureConnected() return the converter directly so play() no longer re-reads outputConverter outside the lock (closes the data race with handleConfigChange). 2. Observer leak (correctness): configure() runs on every LiveEngine start (stop->start reuses the same long-lived engine), so a bare addObserver stacked a new config-change observer each cycle. removeObserver(self) first to keep it idempotent. 3. Dead code (cleanup): dropped TxConnectionState.isReady and its unused Equatable conformance (referenced only by tests); tests now assert needsAttach/needsConnect — the flags production actually branches on. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
A pure rename, kept separate so `git log --follow AGENTS.md` and blame keep the file's history. The next commit makes the wording agent-neutral and recreates CLAUDE.md as a pointer. Refs #843. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
AGENTS.md is now the single source of rules for every contributor, human or coding agent. CLAUDE.md shrinks to a pointer that imports it (@AGENTS.md), so Claude Code still loads the full rules. Every rule, command and gotcha is kept; only the title and the local-notes file name change (AGENTS.local.md, with CLAUDE.local.md still ignored and still working). - Testing gains iOS and desktop subsections with the verified commands, and notes the CI gaps. swift test needs full Xcode (XCTest). - Comments in 12 source files and the .gitignore note now point to AGENTS.md. - README links AGENTS.md from a new Contributing section. - .github/scripts/check-agents-md.sh (with --self-test) and the agents-md workflow fail if AGENTS.md is missing, if CLAUDE.md loses its import, or if any file still refers to CLAUDE.md. Closes #843. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The rig meter callback is live whenever PTT is keyed — during an FT8 over and during a user-initiated tune carrier alike. High SWR during a tune is the expected starting condition (the operator keys a carrier precisely to bring a match down), not a fault, but the SWR halt was firing anyway: it set the lockout, showed the banner, and the SWR_LOCKED gate then blocked the very retune the operator pressed Tune for. Thread a `tuning` flag through the pure `shouldHaltForSwr` gate so it returns false while a tune carrier is up, and pass `transmitSignal.isTuning()` from `onMeterUpdate`. The FT8-over halt path is unchanged. The tune worker keeps its own bounded max-on timeout for PA safety, and the diagnostic log now records "tune (halt suppressed)" so the reason is visible in debug.log. Adds shouldHalt_neverWhileTuning and updates the two existing callers for the new signature. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
fix: don't let SWR protection interrupt a user tune
Move agent instructions to tool-neutral AGENTS.md; CLAUDE.md becomes a pointer
Turn the always-open operate control strip into a Material bottom-sheet drawer, per the "FT8AF app UX redesign" concept (options 3a/3b): - Collapsed peek (3a): rig/CAT status line docked above a rounded-top drawer surface with a drag handle, the primary Call CQ / Stop action, and the TX-period toggle. Tap the handle or swipe up to open. - Expanded sheet (3b): the full control set — Band & Mode, Call CQ (+ MORE) / Hunt, TX Period / Tune / DX, and the power slider — over a scrim, reusing FT8AFBottomSheet (swipe-down / scrim-tap / Back dismiss). The control widgets are extracted from TxStrip into shared internal composables (OperateStatusRow, BandModeRow, CqHuntRow, PeriodTuneDxRow, TxVolumeRow) so the peek, the expanded sheet, and the legacy strip all render identical controls and drive one set of hoisted action lambdas. Shipped behind a "Controls Drawer" setting (Radio & Audio, default ON, mirrors the showTxVolumeSlider pattern); off restores the original always-open strip. The drag-to-settle rule (drawerTargetExpanded) is extracted and unit-tested. Verified on emulator: peek, expand, collapse, and the settings toggle. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Previously the expanded controls were a separate modal (FT8AFBottomSheet)
that slid up over the collapsed peek and reordered the controls, so it read
as two stacked sheets rather than one drawer.
Rework it into a single sheet that grows in place: one overlay whose bottom
is anchored to a reserved peek slot in the main column and whose height
animates between peek and expanded. The primary Call CQ / Stop + TX-period
row is the top of the sheet in both states; Band & Mode, Hunt, Tune / DX,
and the power slider expand into view below it. The handle drags live
(follows the finger) and taps to toggle; a scrim over the content above
(not the tab bar) fades in and taps to collapse.
Also add a compact TX-period variant ("1st"/"2nd", no caption) so the
segmented control reads cleanly in the narrow slot beside Call CQ, and
tighten the peek height so no secondary row peeks through when collapsed.
Verified on emulator: peek, drag/tap expand, collapse.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Move Hunt up beside Call CQ in the always-visible peek (the mockup 3b primary pairing), reusing the shared CqHuntRow. TX period drops into the expanded section on its own row with Tune/DX (PeriodTuneDxRow) — there isn't room for a third control beside the big Call CQ button. Reverts the now-unused compact TX-period variant. - Make it obvious the sheet pulls up: the collapsed handle now shows a grabber bar plus an up-chevron and an amber "More controls" caption that gently bobs; expanded, it shows a down-chevron to collapse. Verified on emulator: peek shows Call CQ + Hunt + the hint; expand reveals Band & Mode, TX Period / Tune / DX, and power. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
feat: collapsing bottom-sheet controls drawer (design 3a/3b)
fix: reconnect iOS TX player node after an audio config change
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## staging #910 +/- ##
=============================================
- Coverage 42.09% 41.73% -0.37%
Complexity 252 252
=============================================
Files 277 279 +2
Lines 33233 33668 +435
Branches 3887 3958 +71
=============================================
+ Hits 13991 14052 +61
- Misses 18957 19329 +372
- Partials 285 287 +2
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.
Promotes the current
devintostaging.Notable changes since last promotion
See the full commit list in the PR's Commits tab.
🤖 Generated with Claude Code