Current status
All work in this issue is complete on main. The reconfiguration, volume-feedback, and text-size fixes were merged in #91; the remaining panel-height and scrolling fix was merged in #125. The panel-height fix is awaiting the next release and is not included in 1.0.4.
Shipped
Verification result
The connected Dell U2725QE and MSI MAG 341C OLED checks are recorded in #91, including manual observations, automated regressions, and measurement limits. Live VoiceOver testing was deliberately skipped; accessibility labels and markers were checked. The numbered protocol below is retained as the original verification procedure.
The panel-height implementation and verification are recorded in #125: native hosting regressions cover long display lists, changing height budgets, and pinned controls; the physical menu check with both external monitors connected looked good. All required CI checks passed before merge. The verification limits are recorded in that PR. The separate image-rendering investigation remains open in #94.
Original investigation
The observations and procedure below describe the original report. Completed portions are identified in the current status above; their recorded verification is linked from the merged PR.
Five defects in the menu-bar panel and the Settings window. The first is visible to anyone who dims a display below the DDC floor and then plugs anything in.
What happens
- Software-dimmed displays flash to full brightness on every reconfiguration. In Candela/App/StatusItemController.swift the reconfiguration handler resets gamma and removes every shade synchronously, and only then loops per display awaiting two HDR reads before re-applying the dim. Each of those reads re-enumerates the display list on a shared actor whose cache was just wiped, so the un-dimmed window grows with the number of displays. Gamma is the default software backend, so this is the common case, not an edge one.
- Nothing bounds the panel's height on a large desk. Candela/Panel/PanelView.swift sets a width and no maximum height, and there is no scroll view anywhere under Candela/Panel. Enough displays with contrast sliders enabled push the footer, which holds Settings and Quit, past the usable menu height, and a menu holding one oversized custom view does not scroll it. The threshold depends on the screen: at roughly 216 points per display block, four externals fit on an ultrawide but three overflow on a 14 inch built-in display.
- Option plus a volume key opens Sound settings and also plays the volume feedback sound. The key-release guard in CandelaKit/Sources/CandelaKit/Input/KeyRouter.swift runs before the modifier checks, so every volume key release returns the release action, and the executor plays the sound without asking whether a step actually happened.
- The panel ignores the system text size, inconsistently. Nearly every string is a fixed point size, but the hover reason, the percent readout and the readout's column width scale with the system setting. At a large accessibility text size those three grow while the labels beside them do not, and because the panel width is fixed, a widened readout steals track width from the slider.
- The Sound card shows a stale output device after a route change. The display hub's Sound card and the diagnostics page both read the default output device from a snapshot that is not observable, so after the system output changes they keep showing the previous device until some unrelated change causes a re-render.
Expected
A reconfiguration should re-apply software dimming immediately after removing it, so a dimmed display does not flash; the panel should cap its height against the screen it opens on and scroll its display list, with the footer always reachable; Option plus a volume key should open Sound settings without playing the step sound, while a plain volume key at maximum or minimum still plays it; the panel should treat text size consistently rather than scaling three elements out of two dozen; and the Sound card and diagnostics page should follow the system output device as it changes.
Notes
- Reconfiguration flash: hoist the HDR pass into its own loop above the gamma reset and the shade removal, so the removal is immediately followed by the re-apply. The alternative of narrowing removal to shades whose drawable key moved is unsafe: a comment at that site states the wholesale removal is unconditional on purpose, to avoid stranding a shade when mirroring engages. The ordering contracts still hold with the hoist (the reconfiguration notice precedes the reads, and the default-table recapture inside the re-apply stays after the reset). Rewrite the comments that describe the old order. This does not touch how dimming preference changes propagate, which has its own required path.
- Panel height: take the maximum height from the status item's screen at the moment the panel opens. The footer, the keep-awake row and the accessibility banner stay outside the scroll view. One thing to check first: whether a scroll view inside an open menu's tracking session receives scroll wheel events at all. That has not been measured here, and the panel's own notes record that an open menu clips the footer rather than growing. Intrinsic content size negotiation with the hosting view is the medium risk in this change.
- Volume key with Option: this is behaviour inherited from an earlier code base and kept deliberately so far, specified verbatim at the time and pinned by a test whose comment records where it came from. Changing it is a documented divergence and needs a decision: keep the inherited behaviour, or diverge and say so. If it changes, take the executor-side variant and gate the sound on whether the preceding key-down for that key routed to a volume step. Do not gate it on the value having changed: that would kill the sound at maximum and minimum, where macOS itself plays it. This leaves the key router and its tests untouched.
- Text size: the cheap fix makes the three scaling sites fixed so the panel is internally consistent, and it is only correct if all three change together (the readout font, the readout column width, and the hover reason). It makes the panel fully non-scaling, which is a deliberate accessibility trade that should be named in the change rather than slipped in. Real scaling is a much larger job and is gated on the panel height question above. No test pins these three sites; the render smoke test asserts only a pixel floor. One invariant to preserve in words: the readout column exists so that "100%" is not truncated, so whatever replaces the scaling metric must still fit it.
- Stale Sound card: the performance framing is wrong and should not be used. The audio provider installs its listeners and primes its snapshot at launch, off the caller's thread, so after launch the read is an uncontended lock read, not a round trip. The real defect is that the snapshot is not observable. Publish an observable mirror on the app model from the existing listener hop, which already hops to the main actor, and read that mirror at the two user-interface sites only. The engine must keep calling the device lookup directly: an invariant states that the audio target is resolved at key-press time, which is what fixes a stale target cache inherited from an earlier code base. This is not a preference and needs no key. The risk is adding a new trigger that re-renders the whole hub.
Hardware verification
- Dell U2725QE: set brightness low enough that the software leg is active (below the point where the DDC register bottoms out), then force a reconfiguration by plugging or unplugging the other display. Watch for a flash to full brightness. Capture at about 10 frames per second across the reconfiguration so the flash is measurable and not just remembered. Positive control: run the same capture before the change and confirm the flash is present, otherwise a clean capture afterwards proves nothing.
- Repeat step 1 with the shade backend forced on through the avoid-gamma escape hatch, so both software backends are covered.
- Repeat with both externals attached so the per-display loop has more than one iteration, which is where the window is widest.
- Panel height: enable contrast sliders, attach the built-in plus both externals, and open the menu-bar panel from the smallest attached screen. Settings and Quit must be reachable, and the scroll wheel must scroll the display list while the menu is tracking. This needs a human look: a window capture clips as an artifact and has produced three different heights for the same panel.
- Route audio output to the MAG's own audio device first (otherwise the volume keys are released to macOS and nothing under test runs). Press Option plus volume up: Sound settings must open and no sound must play. Positive control: press volume up alone at maximum volume and confirm the sound still plays.
- Set a large accessibility text size in System Settings, open the panel, and confirm the percent readout, its column and the hover reason no longer grow past the labels beside them, and that "100%" still fits.
- With the Settings window open on the display hub, change the default output device in System Settings: the Sound card must show the new device within a second with no other interaction. Repeat with the diagnostics page open. Positive control: change it back and confirm the card follows in both directions.
Current status
All work in this issue is complete on main. The reconfiguration, volume-feedback, and text-size fixes were merged in #91; the remaining panel-height and scrolling fix was merged in #125. The panel-height fix is awaiting the next release and is not included in 1.0.4.
Shipped
Verification result
The connected Dell U2725QE and MSI MAG 341C OLED checks are recorded in #91, including manual observations, automated regressions, and measurement limits. Live VoiceOver testing was deliberately skipped; accessibility labels and markers were checked. The numbered protocol below is retained as the original verification procedure.
The panel-height implementation and verification are recorded in #125: native hosting regressions cover long display lists, changing height budgets, and pinned controls; the physical menu check with both external monitors connected looked good. All required CI checks passed before merge. The verification limits are recorded in that PR. The separate image-rendering investigation remains open in #94.
Original investigation
The observations and procedure below describe the original report. Completed portions are identified in the current status above; their recorded verification is linked from the merged PR.
Five defects in the menu-bar panel and the Settings window. The first is visible to anyone who dims a display below the DDC floor and then plugs anything in.
What happens
Expected
A reconfiguration should re-apply software dimming immediately after removing it, so a dimmed display does not flash; the panel should cap its height against the screen it opens on and scroll its display list, with the footer always reachable; Option plus a volume key should open Sound settings without playing the step sound, while a plain volume key at maximum or minimum still plays it; the panel should treat text size consistently rather than scaling three elements out of two dozen; and the Sound card and diagnostics page should follow the system output device as it changes.
Notes
Hardware verification