Skip to content

[RFC] Radio tab: AT chip and quality-coloured status dot while adaptive frame-rate throttle is on #6790

Open

Description

@chibondking

Status: RFC, so the colour changes in #6787 can be ruled on before merge, as GOVERNANCE.md asks for visual-design changes. #6787 is already open and implements this; it waits on this ruling.

Problem

Since the unified title bar (#6198), you can barely tell from the title bar whether adaptive frame-rate throttle is on or capping:

Proposal

On the connected radio's tab:

  1. "AT" chip after the radio's name while "Enable adaptive frame-rate throttle" is ticked. Outlined while armed; filled in the cap's colour while a cap is in force.
  2. Quality-coloured status dot. While, and only while, the chip shows, the dot takes the network-quality colour (the status bar's palette), so the operator can see the conditions the throttle is reacting to. With the throttle off, the dot stays the plain green "connected" colour. A recoloured dot always comes with the chip that explains it.
  3. Words for the same state in the tab's tooltip and accessible description (WCAG 1.4.1).

Items 1 and 3 restore what #6198 lost, and could stand on the "bug fix with a clear root cause" exemption. Item 2 is new: the old lamp showed the cap's tier, not the live grade. Three palette decisions need a ruling:

  • (a) the dot taking the quality palette while the chip shows;
  • (b) the chip's fill in the cap's colour;
  • (c) the dot's heartbeat glow following the dot's colour. This also means the amber discovery pulse now glows amber.

Contrast (measured by the PR review, dot against the active tab's effective background):

Grade light #ddebf2 dark #0c2636
Excellent/Very Good #00cc66 1.75:1 7.30:1
Good #00b4d8 2.02:1 6.33:1
Fair #cc9900 2.12:1 6.03:1
Poor #cc3333 4.22:1 3.04:1
replaced: color.titlebar.status.connected 4.10:1 (#1a8040) 8.48:1 (#4dd87a)

Dark passes everywhere. In the light theme three of four grades are below the 3:1 non-text minimum (docs/a11y.md, WCAG 1.4.11), because networkQualityColor() returns fixed hex values rather than theme tokens. The tooltip's spelled-out grade covers 1.4.1, but not 1.4.11.

If (a) is approved, the proposal is to merge with the hex values (docs/a11y.md allows that as an intermediate state) and file a follow-up to tokenise the quality palette for both the status bar and the tab dot, with light-theme values that pass 3:1.

Cross-platform impact

Everything is in shared Qt widget code (RadioTabBar, TitleBar, MainWindow), painted the same way on Linux, macOS and Windows. The only platform branches in RadioTabBar.cpp (macOS focus and drag handling) are untouched. There is no native chrome integration, no new dependency, and no new settings key; the chip reads the existing AdaptiveThrottleEnabled setting. Tested live on Windows only.

Alternatives considered

  • Chip only, dot stays green (items 1 and 3, no 2). No palette ruling needed, and could merge on the regression exemption. But the operator then can't see the link quality the throttle is reacting to without opening the status-bar tooltip.
  • Dot shows the cap's tier, as the old lamp did. A closer restoration, but it says nothing while the throttle is armed and not capping. The chip already carries the cap's colour.
  • Quality colour on the dot always, AT or not. Rejected by the operator: a colour change with no visible reason. Recolouring only while the chip shows keeps the cause next to the effect.
  • Tokenise the quality palette in this change. Fixes the light-theme contrast, but drags the status bar's colours into this change; better as the follow-up above.

Environment

AetherSDR 26.10.3 (Windows), FLEX-6600 4.2.20. Implemented in #6787.

Activity

  1. added
    claude-activeAetherClaude is actively working on this issue
    maintainer-reviewRequires maintainer review before any action is taken
    on Oct 26, 2026
  2. aethersdr-agent commented on Oct 26, 2026

    @aethersdr-agent
    Contributor

    Triage: maintainer-review — the report is accurate, and the visual surface needs a ruling before #6787 lands

    Root cause, verified in tree

    The throttle indicator still exists; it just has nowhere visible to land.

    MainWindow.cpp:2162-2166 maps the cap to a colour (fpsCapColor: #cc3333 ≤ 4 fps, #cc9900 ≤ 8, else #00b4d8) and pushes it via TitleBar::setThrottleFlashColor() (TitleBar.cpp:1687). That colour reaches the tab through exactly one path — currentBeatColor() (TitleBar.cpp:1666) → RadioTabBar::pulseLink() → RadioTab::m_beatColor — and m_beatColor is consumed at exactly one place: RadioTabBar.cpp:373, as glowBase for the heartbeat glow. So post-#6198 the cap's tier is a tint on a glow that swells and decays in ~850 ms per discovery packet. "Barely tell" is a fair description.

    The "armed but not capping" gap is also real, and it is not a #6198 regression. setThrottleFlashColor is only ever called from the adaptiveThrottleChanged(active, fpsCap) handler, and RadioModel::applyAdaptiveFrameRate() (RadioModel.cpp:8989) only emits that signal on a cap tier transition. Merely ticking the box has never produced any indication, before or after #6198 — so item 1's outlined state is new surface, not a restoration. Worth stating plainly so the ruling's scope is accurate: item 1 is "restore the cap colour somewhere legible" plus "add an armed state that never existed".

    What #6787 (open, not merged) does

    • Item 1 — RadioTab::setThrottleChip() paints an "AT" chip after the name (kChipSizePx 8.5, kChipPadX 4, kChipGap 6, kChipRadius 3): outlined when the tint is invalid (armed), filled at 22 % alpha in the cap colour otherwise. The name gives up width to the chip rather than the chip eliding.
    • Item 2 — RadioTab::dotColor() gains a quality branch below the existing override branch, so discovery amber and link-lost red still speak first. RadioTabBar::applyLinkVisuals() gates it on ours && m_throttleEnabled, so a recoloured dot always arrives with the chip.
    • Item 3 — descriptionLine() appends Network <grade> and the chip text, which feeds both the tooltip and the accessible name.
    • No new hex: fpsCapColor moves to MainWindowHelpers::adaptiveFpsCapColor() and routes through the existing networkQualityColor(), so the throttle tiers and the status bar can't drift apart.
    • currentBeatColor() now returns an invalid colour when unthrottled, so the glow follows the dot instead of forcing green over a red dot.
    • Bridge keys radios.linkQuality / linkQualityColor / adaptiveThrottle / adaptiveFpsCap / tabs[].throttleChip, plus 81 lines in tests/unified_title_bar_test.cpp.

    The PR's claim that unticking the box does not lift a cap already in force is correct: applyAdaptiveFrameRate() returns at its first line when AdaptiveThrottleEnabled != "True", so no adaptiveThrottleChanged(false, 0) is ever emitted after an untick. Keeping the chip up on enabled || m_adaptiveFpsCap > 0 is the right call — hiding it would hide a live cap.

    One defect to fix before merge

    refreshAdaptiveThrottleChip() is called from three places: construction, the adaptiveThrottleChanged handler, and the ConnectionPanel::adaptiveThrottleEnabledChanged signal. It is not called from the disconnect handler, which zeroes the state at MainWindow.cpp:7156-7157:

    1. Box ticked, link degrades → cap 8 fps, chip filled.
    2. Operator unticks the box → refresh runs, chip stays filled (by design, cap still in force).
    3. Disconnect → m_adaptiveFpsCap = 0, but nothing pushes that to RadioTabBar, which still holds m_throttleEnabled = true, m_throttleFpsCap = 8. The chip is hidden only because applyLinkVisuals()'s session gate is false while m_activeId is empty.
    4. Reconnect → session goes true and the stale chip paints filled, tooltip "Adaptive throttle: 8 fps cap", with the box unticked and no cap in force. Nothing clears it for the rest of the session: adaptiveThrottleChanged can't fire (the setting gate), and networkQualityChanged doesn't call the refresh.

    Fix is one line — refreshAdaptiveThrottleChip(); after MainWindow.cpp:7157 — plus a test that reconnect after untick-with-cap leaves radios.adaptiveFpsCap at 0 and tabs[].throttleChip false.

    Why this is on you and not on me

    GOVERNANCE.md:97 reserves "any change to visual design — colors, fonts, spacing, theme, icons" to the maintainer, and :221 says the agent may not autonomously change visual design. The reporter flags item 2, and that is the sharpest question — the old lamp showed the cap's tier, the proposal shows the live grade, so a Fair link with no cap engaged would turn the dot amber where today it stays green. But the ruling needs to cover the whole visual surface, not just item 2: the AT chip is new title-bar chrome at a new type size, and item 2 is a new use of the quality palette on a status dot.

    Items 1 and 3 are the parts with a clear regression story behind them (minus the armed state, which is new). Item 2 stands or falls on your call.

    If the design is approved, the implementation in #6787 is sound modulo the stale-chip defect above, and aetherclaude-eligible here would let me carry that fix in.

    🤖 Generated with Claude Code


    🤖 aethersdr-agent · cost: $2.7619 · model: claude-opus-5


    📍 Citation health check (Foundry Constitution Principle I): 1 resolved, 7 unresolved against /Users/aetherclaude/workspace/AetherSDR at origin/main.

    Unresolved citations (7)
    Citation Reason
    MainWindow.cpp:2162-2166 file not found in workspace
    MainWindow.cpp:7156-7157 file not found in workspace
    MainWindow.cpp:7157 file not found in workspace
    RadioModel.cpp:8989 file not found in workspace
    RadioTabBar.cpp:373 file not found in workspace
    TitleBar.cpp:1666 file not found in workspace
    TitleBar.cpp:1687 file not found in workspace
  3. changed the title [-]Radio tab: show when adaptive frame-rate throttle is armed or capping (lost with the unified title bar)[/-] [+][RFC] Radio tab: AT chip and quality-coloured status dot while adaptive frame-rate throttle is on[/+] on Oct 27, 2026
  4. nigelfenton commented on Oct 31, 2026

    @nigelfenton
    Contributor

    Some numbers for the ruling. I computed them from the PR's colour values and the two bundled themes at #6787's head (11c57b80): the active-tab background composited over the title bar, and WCAG 2.1 relative luminance.

    The dot (non-text, needs 3:1). Your light-theme figures reproduce exactly: Excellent/Very Good 1.75, Good 2.02, Fair 2.12, Poor 4.22. In dark I get the same pass/fail (7.47 / 6.47 / 6.17 / 3.11), with small differences from a slightly different composite background.

    The AT chip's text has its own problem, which the table doesn't cover. A capped chip draws the letters "AT" in the cap's colour, at 8.5 px bold, over a 22 % fill of that same colour. That's text, so docs/a11y.md asks for 4.5:1. adaptiveFpsCapColor() only ever yields Good, Fair or Poor, so these are the only cases that occur:

    Cap colour light dark
    Good #00b4d8 (15 fps) 1.70:1 4.30:1
    Fair #cc9900 (8 fps) 1.80:1 4.34:1
    Poor #cc3333 (4 fps) 3.08:1 2.74:1

    None of the six passes, in either theme. The armed chip is fine: its outline and text use color.text.secondary (5.31:1 light, 6.47:1 dark).

    A fix that keeps (b): keep the cap colour for the chip's outline and fill, but draw the letters in color.text.primary. That gives 8.8–10.2:1 in light and 7.3–9.7:1 in dark for all three caps. The colour still says which tier, and the letters stay readable. The outline is then a non-text part (3:1), with the same light-theme shortfall as the dot.

    On merging with the hex values. docs/a11y.md's allowance is for "concrete hex that meets the ratio", with a follow-up to tokenise it. That's the #3441 precedent, which landed hex that already passed. Three of the four light-theme grades don't meet it, so the doc's allowance as written doesn't cover merging them. That's a call for the ruling, not for me, but it seemed worth saying plainly.

    (c), the glow. With AT off, the glow now takes color.titlebar.status.connected rather than color.accent.success. Both bundled themes give those the same value (#1a8040 / #4dd87a), so nothing visibly changes there. Only a user theme that sets them differently would notice. The visible part of (c) is the amber discovery pulse, as the RFC says.

    73, Nigel G0JKN/W3

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    GUIUser interfaceenhancementImprovement to existing featuremaintainer-reviewRequires maintainer review before any action is takenrfc

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions