Skip to content

Latest commit

 

History

2 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

TimeBar

A macOS menu bar app that counts down to — and up from — the moments you care about. Ship dates, deadlines, birthdays, the day you joined.

Your next event lives in the menu bar as a coloured dot and a short countdown. Click it for the full list, each one ticking down to the second.

● 3d          <- the menu bar: your closest event

┌──────────────────────────────────┐
│ TimeBar                        + │
├──────────────────────────────────┤
│ ● Design review      02:05:10    │
│   Sun, Aug 30 at 16:56  remaining│
│ ● Ship v1.0        3d 04:12:00   │
│   Wed, Sep 2 at 19:03   remaining│
│ ● Joined the team  9y 363d …     │
│   Thu, Sep 1, 2016        elapsed│
├──────────────────────────────────┤
│ Add Event           About · Quit │
└──────────────────────────────────┘

Installing

scripts/build-app.sh
open build/TimeBar.app

Drag build/TimeBar.app to your Applications folder to keep it around. TimeBar has no Dock icon and no window — it lives entirely in the menu bar, and you quit it from the popover footer.

To start it automatically, add it under System Settings → General → Login Items → Open at Login.

Using it

Add an event with + in the header, or Add Event in the footer. Give it a name, a date and time, and a colour. +1h / +1d / +1w / +1mo / +1y jump the date without typing it. Press Return to save, Escape to cancel.

Edit an event by clicking its row. Delete it by hovering the row and clicking the bin, or from Delete in the editor.

About in the footer shows the app version and links to the source code and the author's site.

Events are sorted with the soonest first. Once an event passes it drops to the bottom of the list, greyed out and labelled elapsed, and keeps counting upward — so it doubles as a "time since" tracker.

Reading the countdown

The menu bar shows a single unit — just enough to know where you stand at a glance. The popover shows the whole breakdown.

menu bar popover
An hour out 2h 02:05:10
A few days out 3d 3d 04:12:00
Years past +9y 9y 363d 00:00:04
suffix y d h m s
years days hours minutes seconds

There is no month unit, so m always means minutes and never needs a second look. Two months out simply reads as 59d.

A + prefix means the event has already passed and the number is counting up. The menu bar tracks your next upcoming event; once everything has passed, it shows whichever passed most recently.

Your data

Events are stored on your Mac in UserDefaults under sh.mdr.TimeBar, and reload when the app starts. Nothing leaves the machine and there is no network access of any kind.


Development

Built with Swift Package Manager rather than an Xcode project, so it compiles with Command Line Tools alone — no full Xcode install required.

swift build                   # build
swift test                    # 32 tests (swift-testing)
scripts/build-app.sh [debug|release]   # -> build/TimeBar.app

SwiftPM produces a bare executable, but a menu bar app needs a real bundle for Info.plist (notably LSUIElement, which suppresses the Dock icon) to be honoured. scripts/build-app.sh assembles that bundle and ad-hoc signs it — enough to launch locally. Swap in a Developer ID identity before distributing.

Requires macOS 14+ (the Observation framework) and Swift 6.

Layout

Sources/TimeBarCore/     UI-free domain layer — the whole test surface
  Models/                TimeEvent, EventColor, Countdown, CountdownFormatter
  Services/              EventStoring (UserDefaults + in-memory), TimeProviding
  ViewModels/            MenuBarViewModel
Sources/TimeBar/         SwiftUI shell, kept thin
  Views/                 MenuBarLabel, MenuContentView, EventListView,
                         EventRowView, EventEditorView, ColorPalettePicker,
                         AboutView
  Support/               AppComposition (composition root), AppDelegate,
                         AppInfo
Tests/TimeBarCoreTests/  swift-testing suites

MenuBarViewModel takes its store and clock as protocols, so tests drive the countdown with a stub clock and an in-memory store instead of waiting on real time. Everything worth testing lives in TimeBarCore; the SwiftUI layer holds no logic of its own.

Design notes

No months, so m means minutes. Months are the one unit whose natural abbreviation collides with another, so they are dropped entirely. This falls out of the component set CountdownFormatter asks for — leave .month out of the request and Calendar rolls whole months into the day count for you, so there is no formatting branch that could reintroduce one. A test sweeps 800 days of offsets asserting no label ever contains mo.

Calendar arithmetic, not fixed multiples. Every interval goes through Calendar, so year lengths, leap days and DST transitions come out right — 2028 is a leap year, so 2028-01-01 to 2028-12-31 is 365d where the same dates in 2027 are 364d.

The app sleeps until the label would change. CountdownFormatter.nextChange computes the exact instant the visible text next differs, so an event three days out wakes the process about once an hour rather than 86,400 times a day. Per-second ticking happens only while the popover is open, and a model with no events schedules nothing at all. A wake-from-sleep observer catches the clock up so a long sleep can't strand the label.

The colour dot is a non-template NSImage. MenuBarExtra rasterises its label as a template image, which flattens SwiftUI shapes and SF Symbols to a single tint — a Circle().fill(.orange) renders white. Drawing into an NSImage with isTemplate = false is the only way to keep the event's colour.

The editor replaces the list in place rather than presenting a sheet, and uses .stepperField date pickers and a fixed colour palette. Sheets, calendar overlays and ColorPicker's system colour panel all take key focus away from a MenuBarExtra window and dismiss it mid-edit.

About

A macOS menu bar app that counts down to — and up from — the moments you care about

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages