@@ -49,7 +49,7 @@ final class AppModel {
4949 /// defined by `MenuBarComponent.available(for:)`.
5050 var orderedEnabledItems : [ ( module: MetricID , component: MenuBarComponent ) ] {
5151 availableModules. flatMap { id in
52- MenuBarComponent . available ( for: id)
52+ availableComponents ( for: id)
5353 . filter { enabledComponents [ id] ? . contains ( $0) ?? false }
5454 . map { ( id, $0) }
5555 }
@@ -201,21 +201,35 @@ final class AppModel {
201201 }
202202
203203 /// Component choices worth offering for a module. Battery health and cycle count
204- /// are hidden when this Mac does not report them, so the builder never offers a
205- /// look that can only ever render a dash.
204+ /// and temperatures are hidden when this Mac does not report them, so the
205+ /// builder never offers a look that can only ever render a dash.
206206 func availableComponents( for id: MetricID ) -> [ MenuBarComponent ] {
207+ let components = MenuBarComponent . available ( for: id)
207208 guard let detail = latest [ id] ? . detail else {
208- return MenuBarComponent . available ( for : id )
209+ return components . filter { $0 != . temperature }
209210 }
210- return MenuBarComponent . available ( for : id ) . filter { component in
211+ return components . filter { component in
211212 switch component {
212213 case . health: return detail [ " healthPercent " ] != nil
213214 case . cycles: return detail [ " cycleCount " ] != nil
215+ case . temperature: return temperature ( for: id) != nil
214216 default : return true
215217 }
216218 }
217219 }
218220
221+ /// Temperature belonging to a hardware-domain module, if the SMC exposes a
222+ /// recognized sensor for that domain on this Mac.
223+ func temperature( for id: MetricID ) -> Double ? {
224+ let detail = latest [ . sensors] ? . detail
225+ switch id {
226+ case . cpu: return detail ? [ " cpuMax " ]
227+ case . memory: return detail ? [ " memoryMax " ]
228+ case . gpu: return detail ? [ " gpuMax " ]
229+ default : return nil
230+ }
231+ }
232+
219233 /// Keeps every available module sampled while the menu bar builder is on screen,
220234 /// so its component previews show real values rather than placeholders.
221235 func beginBuilderPreview( ) {
@@ -297,13 +311,23 @@ final class AppModel {
297311 /// Applies one engine pass while preserving the last valid sample across short
298312 /// provider interruptions.
299313 func apply( _ report: SamplingCycleReport ) {
314+ let previousTemperatureModules = availableTemperatureModules
300315 for (id, sample) in report. samples {
301316 latest [ id] = sample
302317 consecutiveSamplingFailures [ id] = 0
303318 }
304319 for id in report. failedMetricIDs {
305320 consecutiveSamplingFailures [ id, default: 0 ] += 1
306321 }
322+ if availableTemperatureModules != previousTemperatureModules {
323+ onModulesChanged ? ( )
324+ }
325+ }
326+
327+ private var availableTemperatureModules : Set < MetricID > {
328+ Set ( [ MetricID . cpu, . memory, . gpu] . filter {
329+ latest [ $0] != nil && temperature ( for: $0) != nil
330+ } )
307331 }
308332
309333 /// Shared state resolver used by menu bar, popover, panel, and Settings preview.
@@ -342,7 +366,8 @@ final class AppModel {
342366 }
343367
344368 /// Samples visible modules plus metrics required by enabled alerts. Temperature
345- /// readings stay active when CPU/GPU is visible because their popovers show them.
369+ /// readings stay active when CPU, memory, or GPU is visible because their
370+ /// popovers and optional menu bar components show them.
346371 private func refreshActiveMetrics( ) {
347372 var active = enabledModules
348373 for (id, rule) in alertRules where rule. enabled {
@@ -351,15 +376,15 @@ final class AppModel {
351376 for (signal, rule) in systemAlertRules where rule. enabled {
352377 active. insert ( signal. metricID)
353378 }
354- if active. contains ( . cpu) || active. contains ( . gpu) {
355- active. insert ( . sensors)
356- }
357379 if onboardingPreviewActive {
358380 active. formUnion ( [ . cpu, . memory, . battery, . network] )
359381 }
360382 if builderPreviewActive {
361383 active. formUnion ( availableModules)
362384 }
385+ if !active. isDisjoint ( with: [ . cpu, . memory, . gpu] ) {
386+ active. insert ( . sensors)
387+ }
363388 engine. setActiveMetrics ( active)
364389 }
365390
0 commit comments