diff --git a/date-wizard/Sources/DateWizard/MenuBarDays.swift b/date-wizard/Sources/DateWizard/MenuBarDays.swift index 2e5766f487..3e42f0d97a 100644 --- a/date-wizard/Sources/DateWizard/MenuBarDays.swift +++ b/date-wizard/Sources/DateWizard/MenuBarDays.swift @@ -104,14 +104,18 @@ final class MenuBarDays { if (date != nil) != had { fit?.updateRungs(fitRungs(hasNext: date != nil)) } } - /// Live H:MM:SS countdown to the next appointment ("now" at zero). Far-off - /// events (100h+) collapse to whole days, where ticking seconds are noise. + /// Short, unit-labeled countdown to the next appointment: the two most + /// significant non-zero units ("2d3h", "20h41m", "5m30s", "45s"), "now" at + /// zero. Seconds tick live once the event is under an hour away. private func countdownText() -> String? { guard let d = nextEventDate else { return nil } let secs = Int(d.timeIntervalSinceNow) if secs <= 0 { return "now" } - if secs >= 100 * 3600 { return "\(secs / 86400)d" } - return String(format: "%d:%02d:%02d", secs / 3600, (secs % 3600) / 60, secs % 60) + let days = secs / 86400, h = (secs % 86400) / 3600, m = (secs % 3600) / 60, s = secs % 60 + if days > 0 { return "\(days)d\(h)h" } + if h > 0 { return "\(h)h\(m)m" } + if m > 0 { return "\(m)m\(s)s" } + return "\(s)s" } // Re-render the badge every second so the H:MM:SS readout ticks live. It's a diff --git a/date-wizard/Sources/DateWizard/WizardRoster.swift b/date-wizard/Sources/DateWizard/WizardRoster.swift index dd2d89eace..d4dab7dbc4 100644 --- a/date-wizard/Sources/DateWizard/WizardRoster.swift +++ b/date-wizard/Sources/DateWizard/WizardRoster.swift @@ -102,19 +102,21 @@ func wandGlyph(pointSize: CGFloat = 15, badge: String? = nil, dot: Bool = false, } let badge = badge! - // Countdown rung: small accent-colored text, left of the wand and raised to - // its tip (top-aligned), no pill/outline. + // Countdown rung: small white text to the RIGHT of the wand, raised toward + // its tip (top-aligned), no pill/outline. (Plain white/black — not accent — + // reads cleaner at this size; the accent stays on the presence dot.) let font = NSFont.systemFont(ofSize: 7.5, weight: .bold) - let textAttrs: [NSAttributedString.Key: Any] = [.font: font, .foregroundColor: accent] + let textColor = dark ? NSColor.white : NSColor.black + let textAttrs: [NSAttributedString.Key: Any] = [.font: font, .foregroundColor: textColor] let textSize = (badge as NSString).size(withAttributes: textAttrs) - let size = NSSize(width: textSize.width + gap + tinted.size.width, + let size = NSSize(width: tinted.size.width + gap + textSize.width, height: max(tinted.size.height, textSize.height)) let out = NSImage(size: size) out.lockFocus() - (badge as NSString).draw(at: NSPoint(x: 0, y: size.height - textSize.height), - withAttributes: textAttrs) - tinted.draw(at: NSPoint(x: textSize.width + gap, y: (size.height - tinted.size.height) / 2), + tinted.draw(at: NSPoint(x: 0, y: (size.height - tinted.size.height) / 2), from: .zero, operation: .sourceOver, fraction: 1) + (badge as NSString).draw(at: NSPoint(x: tinted.size.width + gap, y: size.height - textSize.height), + withAttributes: textAttrs) out.unlockFocus() out.isTemplate = false return out