Repository navigation
fix(gui): dimmed Radio Audio Outputs rows look dimmed. Principle VIII. - #6388
Conversation
Radio Setup -> Audio -> Radio Audio Outputs: the label, value and Mute button of a disabled Headphone or Line Out row carry stylesheets with no :disabled rule, so only the slider shows the row is unavailable. They take color.control.unavailable on :disabled, the pattern of the TX timing fields. The enabled look, the predicates and the Line Out logic are unchanged. The two headphone reason sentences move next to the predicate in MixerControlAvailability.h; the title bar, the Radio Setup row and both tests read them from there. Refs aethersdr#5482, aethersdr#6353 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
There was a problem hiding this comment.
Issue fit
Yes, for the part it claims. #5482 is the title-bar headphone capability gate; #6353 landed the dimming and the reasons, and its acceptance item "visibly dimmed" was only half-true — the slider dimmed, the label, value and Mute button did not. This closes that gap for both rows of Radio Audio Outputs and hoists the two reason sentences to one owner. The Refs, not Fixes, is right: #5482's own "out of scope, deliberately" item (labelling whose output each slider is) is untouched and still needs a maintainer ruling, so #5482 should not close on this.
I verified every substantive claim in the body I could check statically, and all of them held — details in "What I tried to break".
Scope
| File | What it changes | Claimed? | Verdict |
|---|---|---|---|
src/gui/MixerControlAvailability.h |
Adds the two inline constexpr char[] reason sentences beside the predicate |
Yes | In scope |
src/gui/RadioSetupDialog.cpp |
makeValueLabel gains an optional extraRules; two :disabled rule constants; six widgets in the Headphone and Line Out rows take them; the headphone lambda reads the header constants |
Yes | In scope |
src/gui/TitleBar.cpp |
MixerControlAvailability.h include; two inline literals replaced by the header constants |
Yes (the hoist) | In scope |
tests/mixer_control_availability_test.cpp |
Two checks: the title bar's descriptions equal the header's sentences | Yes | In scope |
tests/radio_setup_headphone_output_gate_test.cpp |
connectDemo() extracted from case 5; dimmedRowsLookUnavailableInBothThemes |
Yes | In scope |
tests/tests.cmake |
Comment text on the existing registration block only — no target, source, environment or TIMEOUT change |
Yes | In scope |
Everything in the diff is explained by the issue and the PR body. Checked specifically for the usual smuggling shapes and found none: no new public surface (the two constants are internal UI text, no protocol verb, no settings key, no capability field), no removed guard, no changed default, no CHANGELOG.md entry, no build-config or CI churn, and the one refactor (connectDemo) is a verbatim extraction of case 5's body.
On the CodeGuard list: the harness handed over a 56-file audit list and ~718 findings, but only three of those files are in this PR at all, and none of the quoted hits is in the diff — the appimage.yml / macos-dmg.yml / static-checks.yml lines are a stale wide snapshot of untouched files. I ran the required security-audit subagent against the PR head; it reported no findings in this PR's diff, and I confirmed its reasoning on the one shape that would matter here: the two new QSS fragments (RadioSetupDialog.cpp:146-149) are compile-time literals concatenated with other literals, so no radio-, model- or operator-supplied string ever reaches applyStyleSheet() on these paths — there is no stylesheet-injection vector. The two reason constants reach only setToolTip/setAccessibleDescription, with no format string and no arg(). The CS-VLN-CMDI-BACKTICK family hits on MixerControlAvailability.h:8 are the known false positive: a // comment Markdown-quoting the Flex verb `mixer headphone|lineout ...`. That is a clean result for this diff, not a clean bill for the 53 files the PR does not touch.
Socket tests: none added, none removed. The new case uses the existing never-dialled Demo pattern (QHostAddress::LocalHost, port 4992, connectToRadio on SimBackend, which the file's header comment already documents) and the injected NoCommandPlaneBackend. No QTcpServer/QUdpSocket/bind/listen/connectToHost, and no fake radio, amplifier or tuner peer.
Blockers
None.
Nits
All non-blocking.
-
src/gui/RadioSetupDialog.cpp:4763-4764and:4820-4821—:checkedand:disabledcollide on a muted unavailable row."QPushButton:checked { background: #8b0000; color: {{color.text.primary}}; }" + kUnavailableButtonRuleBoth selectors carry one pseudo-class, so they tie on specificity and the appended
:disabledrule wins oncoloronly because it comes last.background: #8b0000is not overridden. So a row whose Mute is engaged when the radio turns out to have no headphone output renders dark-red-on-#3a4a5a— the "engaged" background with unavailable text, which reads as broken rather than unavailable. Reachable wheneverm_model->headphoneMute()(orlineoutMute()) is true on a no-command-plane radio;#8b0000is pre-existing, not this PR's. The new test cannot catch it: it assertsQCOMPARE(dim.mute->isChecked(), live.mute->isChecked())and both default to unchecked, so the checked+disabled combination is never rendered. If you want it closed, an explicit rule beats relying on source order:static const QString kUnavailableButtonRule = "QPushButton:disabled { color: {{color.control.unavailable}}; }" "QPushButton:checked:disabled { background: {{color.background.1}}; " "color: {{color.control.unavailable}}; }";
(I have not built that; the background token is a guess at the right neutral and is the maintainer's call.)
-
tests/radio_setup_headphone_output_gate_test.cpp:177-178with its caller at:334— a size mismatch is reported as the wrong defect.differingPixelsreturns-1for differing sizes or a null image, and the caller folds that intoif (diff <= 0) failures << ": dimmed renders as live". If the two dialogs' widgets ever differ in geometry, the failure message names the opposite problem. Cheap:if (diff < 0) failures << where + QStringLiteral(": regions differ in size"); else if (diff == 0) failures << where + QStringLiteral(": dimmed renders as live");
-
tests/radio_setup_headphone_output_gate_test.cpp:358— the theme restore is skipped on an early return.QVERIFY2inside the loop returns from the slot, leaving the process on Default Light andAppSettings["ActiveTheme"]written to it. Harmless today because this is the last slot, but it is a trap for whoever adds the next one; a small RAII guard aroundstartThemewould make it unconditional. -
Per-PR CI does not execute this test, so the pixel assertions are one-host evidence.
ci.ymlhas no unfilteredcteststep — every-Rthere is the frozen.github/ci-test-gate.txtset (cross_needle_meter_test,mac_nr_filter_test, the DV group,asr_gpu_probe_test), andradio_setup_headphone_output_gate_testis in none of them. So the five green checks prove the new code compiles on Linux, macOS and Windows; the first real run ofdimmedRowsLookUnavailableInBothThemeson another platform isfull-suite.ymlafter merge. The body is honest about "macOS, offscreen platform only", so this is a note on where the risk lands rather than a missing disclosure — and the one assertion I would watch there is the zero-tolerance cross-instance one (see below), not the relative ones. -
The Line Out reason sentence stays an inline literal at
src/gui/RadioSetupDialog.cpp:4785, so the header's "Every site that dims the pair, and the tests, read them from here" is true of the headphone pair only. The body discloses this and I verified the claim behind it:git grepfinds that sentence in exactly one place, no other source file and no test. Fine as scoped; worth a sentence in the header comment if the two rows ever diverge.
What I tried to break
Everything below is reasoned from the PR head checkout at ee179f67; I have no build and no test run, so nothing here was reproduced at runtime.
- Every factual claim in the body. The sentences really are character-identical — TitleBar's two removed literals and the header constants match byte for byte, and the dialog's old
prefix + suffixconcatenation reconstructs each one exactly.kValueStyleTemplatestill has exactly one reference (makeValueLabel), as its own comment demands.applyLabelStyleis unchanged, and the two inlined replacements are faithful:applyLabelStyle(w)isapplyStyleSheet(w, kLabelStyleTemplate)and nothing else (RadioSetupDialogCommon.h:43-46), so no other page of the dialog moves. The cited precedents exist:RadioSetupDialog.cpp:2575(TX timingQLineEdit:disabled, #6166) andWfmApplet.cpp:58. - Is this a preference restyle? No —
docs/style/theme-style-guide.md§4a namescolor.control.unavailableas the token for the unavailable state, in a table, and the PR quotes that section's contrast figures (2.09:1 dark, 1.77:1 light) correctly rather than inventing better ones, while adding no colour literal and not touching the token. §4a's own text says colour alone cannot carry the distinction, which is whyaccessibleDescriptionis mandatory — and that channel landed in #6353 and is unchanged here. This is a fix toward documented canon, not taste. - Why not
ControlAvailabilityRegistry? That registry exists for exactly this doctrine (registerWidget(widget, reason, predicate)→ token treatment viasetWidgetForegroundToken+ accessible description), so hand-rolling a third copy looked like a seam-in-the-wrong-place finding. It is not: the registry's predicate is(bool connected, const RadioCapabilities&), and command-plane reachability is not aRadioCapabilitiesfield. The file already states that twice, at:2585and:5275— "By hand, not ControlAvailabilityRegistry: command-plane reachability is not in RadioCapabilities." The PR is consistent with the established exception. - Would the new test pass against the unfixed code? No, and I checked why rather than taking the mutation table on faith. On
mainthe three widgets carry an explicitcolor:with no:disabledrule, which Qt applies in both states — so dimmed and live render identically anddiff <= 0fires on all twelve regions, which is exactly the zero-pixel measurement the #6353 review reported. The author's fail-before is a mutation rather than a run onmain(the test now reads the header constants, whichmainlacks) and the body says so. - Does the test rely on something CI will not have? This was my main blocker candidate: the new case is the first in this file to call
setActiveTheme, and a missing theme would make it red on the only lane that runs it. It holds up —resources/resources.qrc:36-38shipsdefault-dark.jsonanddefault-light.json,tests.cmake:3746already compiles${THEME_TEST_RESOURCES}into this target, andThemeManager's constructor runsscanAvailableThemes()then activates the saved theme or"Default Dark", soactiveTheme()is never empty and the final restore cannot be handed an empty name. - Lifetime and leakage across the theme loop.
RadioModel, both dialogs,partsanddimmedImagesare all declared inside thefor (theme)body, so iteration 2 holds no pointer into iteration 1's destroyed widgets. The re-enable path is the pattern case 3 already uses (backend->connected = false; emit connectionStateChanged(false)), and I confirmed it actually works:RadioModel::isConnected()falls through tom_backend->isConnected()(RadioModel.cpp:2794) with no cached flag, andapplyHeadphoneAvailability/applyLineoutAvailabilityare both connected toconnectionStateChanged, so the widgets really do re-enable. - Cross-platform fragility of pixel equality — mostly a non-issue, because the dimmed-vs-live and render-twice comparisons are relative within one host, so font and rasterisation differences cancel;
ci.yml:822-826records that offscreen rendering was verified bit-identical across four platforms forcross_needle_meter_test. The one genuinely strict claim isvsDemo == 0at:352-353: a re-enabled HL2 widget must render pixel-identical to a separate dialog's widget. The author guarded the obvious divergences (same size, same value text, same mute state, both asserted before the comparison). The residual mechanism I could not rule out is window-activation state leaking into the paint — twoshow()n dialogs, and Fusion draws focused/default buttons differently — but Mute is checkable with no focus by default, so I rate this low and did not raise it above the CI note. - Sibling rows left behind. The Front Speaker row at
:4875carries the same:checkedrule and no:disabledrule, but it is never gated by an availability predicate, so it cannot be disabled; the body says so and the code agrees. No other Radio Audio Outputs widget is dimmed. - Could not check: the rendered appearance on a real screen, the test's wall-clock against the existing
TIMEOUT 60(the case adds two dialog constructions and a third Demo connect with a 5 sQTRY; the body reports the suite green on the author's Mac, which is the only evidence either of us has), and the two forward notes from #6353's review, which are PR review text — I relied on the body's account of them.
Recommendation
Approve with nits. This is a narrow, canon-anchored fix with an unusually honest body: every claim I could test statically held, the token and pattern come from the style guide and an in-tree precedent, the hoist is byte-for-byte, and the test genuinely fails against the unfixed styling rather than asserting the implementation back to itself. The four nits are all cheap and none of them breaks a user. Concrete next step: if you want the :checked:disabled background closed, it is a two-line addition to kUnavailableButtonRule and belongs here; otherwise the diff < 0 message split and the theme-restore guard can ride along or follow, and it is worth deciding whether radio_setup_headphone_output_gate_test running only in full-suite.yml is acceptable for a pixel-exact test or whether this one should be bumped to CI's gate list.
Thanks for the detailed limits section and the named mutation — the measured pixel table and the explicit "nobody looked at the dialog on a screen" made this much faster to review than it would otherwise have been.
🤖 aethersdr-agent · cost: $14.8948 · model: claude-opus-5
|
Cisco CodeGuard — static analysis of this PR (727 finding(s))
Automated static scan by Cisco DefenseClaw CodeGuard on the changed files. Advisory — some may be false positives; the review above verifies them. 🤖 aethersdr-agent · cost: $15.2694 · model: claude-opus-5 |
Review of aethersdr#6388, nit 1: :checked and :disabled tie on specificity, so a Mute engaged on an unavailable Radio Audio Outputs row kept the engaged background under unavailable text. QPushButton:checked:disabled now sets the row's own unchecked background, color.background.1, and the unavailable token. A control renders in one of three states (theme-style-guide.md, Three-state controls), and this one is unavailable: engaged or not, it renders the same. The test engages both rows' Mute in both themes with the toggled signal blocked, so no model setter runs and commandDropped stays at 0. The dimmed button has no #8b0000 pixel, the live one does, and the dimmed button renders as it does not engaged. Nit 2: a size mismatch reports "regions differ in size", not "dimmed renders as live". Nit 3: an RAII guard restores the starting theme on every exit from the slot. The header comment says the Line Out reason is not one of the hoisted sentences. Refs aethersdr#5482, aethersdr#6353 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Thanks for the review. Fixed in
On the gate list: I have not asked for this test to join the per-PR set. That is the maintainer's call; the body says the pixel figures are one host. Full ctest at 73, ON8ST |
rfoust
left a comment
There was a problem hiding this comment.
The follow-up to #6353 addresses the remaining visible-dimming gap in the Headphone and Line Out rows and centralizes the unchanged headphone reason text. No correctness blockers found. The operator explicitly accepted, as maintainer, the checked+disabled Mute presentation: checked state remains stored but unavailable Mute buttons render identically whether checked or unchecked. That design question is settled for this review.
Scope:
| File/group | Claimed? | Verdict |
|---|---|---|
| MixerControlAvailability.h and TitleBar.cpp | Shared reason text | In scope; wording unchanged |
| RadioSetupDialog.cpp | Disabled styling | In scope; predicates, command routing and enabled styles unchanged |
| Two test files | Rendering and wording coverage | In scope |
| tests.cmake | Coverage comment | In scope; registration unchanged |
No unrelated changes, new settings, changed defaults, removed guards, or CHANGELOG entry. Existing user documentation already describes unavailable output controls as dimmed with a reason.
Blockers: none.
Non-blocking nit: add the explicit QSignalBlocker include in the rendering test (inline suggestion).
Verification: reconstructed merge of head c0bfd88 with main f6cd464; ARM64 macOS build with Qt 6.12 and RADE enabled, host/system processors and executable verified ARM64, no RNNoise x86 sources. Both radio_setup_headphone_output_gate_test and mixer_control_availability_test passed. Removed both disabled-style rule strings: the rendering case failed, all 12 unchecked regions rendered identically to enabled controls, and disabled checked Mute buttons retained 665 red pixels. Restored the PR code and both tests passed again. Registration, CI gate, touchpoint manifest and generated reference checks passed. No model-invocable automated review skill was available; performed a second manual lifecycle/failure-path pass and verified prior bot findings rather than adopting them.
The running offscreen app was driven only against DEMO-0001 through a dedicated authenticated socket and isolated settings. whoami reported PID 5337, identity pr-6388-review, txAllowed false; get radio reported AetherSDR Demo, DEMO-0001, transmitting false. Opened Audio settings, inspected captures in Default Dark and Default Light after a live theme switch, closed/reopened the dialog, and asserted all eight output-row widgets remained enabled. Closed the owned instance afterwards. Disabled rows were exercised by injected-backend tests; Demo has a command plane. No live hardware, custom themes, or Linux/Windows runtime rendering was tested.
The attacks that held: exact wording preservation; checked-state rendering without mutating model state; disconnect restoration of the same widgets; theme changes and dialog recreation. The five published checks passed on this head, but their merge base is older than the local tested merge. Main subsequently advanced to aa0dd0e in unrelated files; that newer merge was inspected for overlap but not rebuilt. No unresolved review threads were returned.
Approve with the non-blocking include nit. The maintainer has accepted the only outstanding visual choice.
…VIII. (#6403) Refs #6387, #6388 Answers rfoust's three optional notes on the approving reviews of #6387 and #6388. ## Limits - One documentation sentence, one comment and five include lines. No behaviour change: no statement, declaration or test registration is touched, and the five headers were already reached through `<QtTest>` and `<QApplication>`. - Built and run on one Mac (macOS, Qt 6.12.0). Per-PR CI compiles these tests but does not run them. The Linux rule the sentence describes is not run on Linux here; it is exercised by `audio_device_preference_policy_test` on this host. - The documentation now names the five ID patterns used by the Linux hot-plug filter. The source matches substrings or a prefix, including custom names; it does not establish any audio server's default device names. - The include pass covers the Qt classes #6387 and #6388 named first in the files they touched. Transitive includes that were there before them are listed below and left alone. ## Change 1. #6387, `docs/user/docs/audio-settings.md:28`: > this reads as applying to all audio-server virtual devices, but the > exception depends on five ID patterns. A custom-named virtual device > outside those patterns restores live. The reviewer's suggested sentence was the starting point. The follow-up commit names all five matching ID patterns, including monitor sources and custom names. `isLinuxTransientAudioDeviceId` leaves those ids out of the arrival list, so a saved device matching one is never seen arriving; at startup the saved id is looked up among all enumerable devices. 2. #6387, `src/core/AudioDevicePreferencePolicy.h:54`: > this comment should describe test portability without claiming every > lane runs it. The suggested wording is taken as written. 3. #6388, `tests/radio_setup_headphone_output_gate_test.cpp:38`: > Include it directly rather than relying on QtTest to supply it > transitively, per the repository explicit-include convention. `<QSignalBlocker>` is included. The same pass over both PRs found four more Qt classes #6388 named first in that file without their header: `QColor`, `QCoreApplication`, `QList`, `QStringList`. They are included too. Found by the same pass and not changed, because the use predates both PRs: `QString` and `QWidget` in that test; `QString` in `mixer_control_availability_test.cpp`, `RadioSetupDialog.cpp` and `TitleBar.cpp`; `QByteArray` and `QList` in `MainWindow.cpp`. The other files of #6387 include what they name. ## Evidence Full ctest before the documentation-only follow-up, at `ac0dd8f6`, `-j8`: 770 of 772 passed (4 skipped: the `nr_stereo_independence_*` set), 2 failed: - `peripheral_auth_dialog_interaction_test`: "dialog 960x680, viewport 658, page minimum 784" (#6008); fails on clean main on this Mac, also on the serial rerun. This diff touches no dialog file. - `vkamp_connection_test`: one of its two known checks, "commands after a disconnect are dropped, not queued" (#5254), at `-j8`; it passed on the serial rerun. `radio_setup_headphone_output_gate_test`, `audio_device_preference_policy_test` and `mixer_control_availability_test` passed. `tools/docs/gen_reference.py --check`, `check_test_registration.py --strict` and `gen_touchpoint_manifest.py --check` pass. At the follow-up commit `f7dd95c9`, `npm ci && npm run build` in `docs/user/` passed, and the source checks remained green. 73, ON8ST 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: Robbie Foust <rfoust@gmail.com>
Refs #5482, #6353
This is the follow-up to the two forward notes in the approving review of #6353.
Limits, first
QWidget::grab()in a test. Nobody looked at the dialog on a screen, and there is no screenshot.applyLineoutAvailabilitylambda are unchanged. Only the three stylesheets of the row gain a:disabledrule, the same one the Headphone row gets, so the two rows keep agreeing.c0bfd883). An engaged Mute on an unavailable row took the unavailable text on the engaged red background. It now takes the row's own unchecked background (color.background.1), so the engaged state is not shown while the row is unavailable. §4a names no look for a control that is unavailable and also has state, so whether the state should stay visible is the maintainer's call. The checked state is still on the widget for accessibility clients.color.control.unavailableis 2.09:1 (dark) and 1.77:1 (light) by the style guide's own table (§4a). This PR uses the token and does not change it.src/gui/RadioSetupDialog.cpp:4774-4775at9fabcbf8); no other source file and no test repeats it.Refs, notFixes: closing M0 item 3 follow-up: the title-bar headphone slider is a second unconverted site for mixer headphone gain/mute #5482 stays your call.What was wrong
At
9fabcbf8, in Radio Setup → Audio → "Radio Audio Outputs", a disabled row shows it on the slider only. The other three widgets of each row carry their own stylesheet, and none has a:disabledrule:kLabelStyleTemplate,QLabel { color: {{color.text.primary}}; … }(src/gui/RadioSetupDialogCommon.h:31-32)src/gui/RadioSetupDialog.cpp:4740/:4794kValueStyleTemplate,QLabel { color: {{color.accent.bright}}; … }(RadioSetupDialog.cpp:129-130):4745/:4799QPushButton { … color: {{color.text.primary}}; … }plus:checked:4751-4753/:4805-4807:4742/:4796An explicit
colorwith no:disabledrule also applies when the widget is disabled, so the label, the value and the Mute button render the same in both states. The review of #6353 measured 0 differing pixels on them; the test here reproduces that (below).The two headphone reason sentences were written out three times:
src/gui/TitleBar.cpp:1297-1302,src/gui/RadioSetupDialog.cpp:4826-4834, andtests/radio_setup_headphone_output_gate_test.cpp:134-139.The change
The look. A disabled label, value or Mute button of the two rows takes
color.control.unavailable, the token §4a ofdocs/style/theme-style-guide.mdnames for the unavailable state:This is the pattern already in the tree for the TX timing fields (#6166,
RadioSetupDialog.cpp:2565-2567,QLineEdit:disabled) and forWfmApplet.cpp:58. The rules go throughThemeManager::applyStyleSheet, so they follow a theme switch like their neighbours. No colour literal is added. The enabled look is unchanged: the rule is appended to the existing template, and only for these six widgets.makeValueLabeltakes an optional extra rule sokValueStyleTemplatekeeps its single reference. The sharedapplyLabelStyleis not changed, so no other page of the dialog moves.The wording.
src/gui/MixerControlAvailability.hnow holds the two sentences next to the predicate, askHeadphoneLevelUnavailableReasonandkHeadphoneMuteUnavailableReason. They areconstexpr char[], so the header stays free of Qt.TitleBar::setHeadphoneAvailable, the Radio Setup lambda and both tests read them from there. The text itself is unchanged, character for character.No predicate, no capability field, no default and no user-visible string changes.
docs/user/docs/audio-settings.mdalready says the controls "are dimmed with the reason in the tooltip", which stays true.Tests
tests/radio_setup_headphone_output_gate_test.cppgainsdimmedRowsLookUnavailableInBothThemes(offscreen, no socket). For each of Default Dark and Default Light it builds the dialog on the HL2 capabilities (both rows dimmed) and on the Demo (both rows live), and for the label, value and Mute button of each row:grab()and counts differing pixels, exactly, no tolerance. It requires more than 0 between dimmed and live;color.control.unavailablehas in the active theme.With the change, differing pixels (measured at
6c83ac5a, the same diff before it was rebased onto currentmain; the test that asserts them passes at the head), dimmed vs live, identical in both themes:Fail-before. Named mutation: this commit with both
:disabledrule strings emptied, which is main's styling. Full build and ctest. All twelve regions read 0 differing pixels, as in the review:The test could not be run on unmodified main as it stands, because it reads the sentences from the header. That is why the fail-before is a mutation and not "the test alone".
The wording.
radio_setup_headphone_output_gate_testcompares the row against the header's two constants.tests/mixer_control_availability_test.cppgains two rows: the title bar's mute and slider descriptions equal the header's sentences. Its existing rows still check the content independently ("Unavailable:", "no headphone output", "speaker button", "master volume").ctest
macOS (Apple Silicon), Qt 6.12.0,
ENABLE_ASR=OFF, full suite at-j8, failures rerun serially, onc0bfd883(this PR's head):vkamp_connection_testwith only its two known checks (Replace retained socket fixtures with injected and opt-in coverage #5254).peripheral_auth_dialog_interaction_testfails, at-j8and serially: "Clear code or Status is clipped at the default dialog size",dialog 960x680, viewport 658, page minimum 784. This is the host-dependent failure of feat(peripherals): authenticate direct TGXL, PGXL, and Antenna Genius connections #6008 (diagnostic in test: drop two host-dependent assumptions that fail on macOS. Principle VIII. #6134): the Peripherals page is as wide as the longest serial-port name on this Mac. The page minimum is 784, the same figure as on clean main here. This PR does not touch the Peripherals page.radio_setup_headphone_output_gate_test(6 passed),mixer_control_availability_test,titlebar_headphone_mute_test,unified_title_bar_testandradio_setup_label_theme_token_testpass.Static checks, run locally:
tools/check_a11y.pyonRadioSetupDialog.cpp9 findings before and 9 after,TitleBar.cpp0;tools/audit_colours.pysummary identical before and after;gen_touchpoint_manifest.py --checkanddocs/gen_reference.py --checkup to date; thetools/check_*.py --strictset passes (check_codeql_coverage.pyneeds a build directory and was not run).73, ON8ST
🤖 Generated with Claude Code