Skip to content
AsQqqqPublic

About

Work flow timer

Resources

Stars

0 stars

Watchers

1 watching

Forks

Latest commit

 

History

5 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 

Repository files navigation

WorkFlow

Platform Swift UI License

A lightweight menu bar countdown timer for macOS. It lives in the menu bar (with a Dock icon), no main window. Click the menu bar item or the Dock icon, pick a duration, and watch the countdown next to the clock. When time is up the title flashes FINISH in red/white and a sound loops for ~10 seconds.

Why "WorkFlow"? That's just the Xcode project / product name. The app itself is a menu bar timer.

Made with the support of TradeCloud. Without TradeCloud, this app would not exist.


Features

  • Menu bar + Dock - a menu bar item plus a Dock icon; click either one to open the timer (also appears in Cmd+Tab).
  • Quick-start presets (5 / 10 / 25 / 45 min) and a custom H:M:S input.
  • Pause / Resume / Reset from a click-through menu while running.
  • FINISH alert - the title flashes red/white (~2 Hz) with a looping sound.
  • Sleep-proof - the remaining time is computed from a wall-clock Date() delta, so it stays correct across system sleep, not from counting Timer ticks.
  • No width jitter - monospaced digits keep the item from resizing as the numbers change.
  • Optional notification via UNUserNotificationCenter.

States

State Menu bar shows Click does
Idle PRESS ME Opens the popover (presets + custom + START)
Running MM:SS / HH:MM:SS Opens a menu: PAUSE, RESET, Quit
Paused `
Finished flashing FINISH + sound Any click stops the alert and returns to Idle

After 10 seconds the Finished state returns to Idle automatically.

Requirements

  • macOS 13 (Ventura) or later
  • Xcode 15+ (the project uses the Xcode 16+ synchronized-folder format)

Build & Run

  1. Open WorkFlow.xcodeproj in Xcode.
  2. Select the WorkFlow scheme and press ⌘R.
  3. Look at the right side of the menu bar, next to the clock - you'll see PRESS ME.

There is no main window - that is intended. The app shows a Dock icon and a menu bar item; click either to open the timer. To quit, choose Quit from the menu bar item, the popover, or ⌘Q.

If you build from the command line instead, the sources compile cleanly under swiftc with -strict-concurrency=complete and even Swift 6 language mode.

Usage

  • Start: click PRESS ME → tap a preset (starts instantly) or type H:M:S and press START.
  • While running: click the countdown → PAUSE / RESET.
  • When finished: the title flashes FINISH and a sound loops; click anywhere on the item to stop it early.

Project structure

WorkFlow/
├── WorkFlowApp.swift         # @main App entry, windowless Settings scene + app delegate adaptor
├── AppDelegate.swift         # wiring + clean teardown on quit
├── TimerEngine.swift         # state machine + Date-delta time logic
├── StatusBarController.swift # NSStatusItem: title rendering, clicks, popover, menus
├── TimerPopoverView.swift    # SwiftUI popover: presets + H:M:S + START
├── AlertPlayer.swift         # looping alarm sound (NSSound)
└── Notifier.swift            # optional UserNotifications alert

Architecture: AppKit owns the status item (needed for attributedTitle with a .backgroundColor to flash, and for raw click handling), while SwiftUI renders the popover content via NSHostingController. TimerEngine is UI-agnostic and exposes only its state and an onUpdate callback.

Technical notes

  • Sleep resilience: the countdown and the 10s finish window are both measured with Date() deltas (endDate.timeIntervalSinceNow), so a missed tick during sleep self-corrects on the next tick.
  • .common run loop mode: timers are added with RunLoop.main.add(timer, forMode: .common) so the countdown keeps updating while a status bar menu is open.
  • Flashing: the finished title is redrawn from a 0.25s timer, alternating NSAttributedString .backgroundColor between red and white (~2 Hz).
  • Concurrency: classes are @MainActor and timers use the target/selector API, so the code is clean under strict concurrency / Swift 6 mode.

Troubleshooting

I don't see the item in the menu bar

The item is created correctly - it is just hidden. On a MacBook with a notch, and/or with a menu bar manager such as Hidden Bar / Bartender / Ice, a newly added status item can land in the hidden/overflow zone behind the notch.

Fixes:

  • Expand the hidden section (click the Hidden Bar / Bartender chevron).
  • Hold ⌘ (Command) and drag menu bar items to reorder - pull PRESS ME out from behind the notch into the visible area.
  • Temporarily quit other menu bar apps to free up space, or switch focus to an app with a short menu (e.g. Finder) so the bar is less crowded.

The finish sound doesn't play

The app runs sandboxed. System sounds work, but if NSSound("Funk") is unavailable it falls back to NSSound.beep(). Make sure the system volume isn't muted.

Credits

This app was built with the support of TradeCloud. Without TradeCloud, this project would not exist. Thank you.

License

MIT © 2026 danila


WorkFlow (по-русски)

Лёгкий таймер обратного отсчёта в строке меню для macOS. Живёт в строке меню (с иконкой в Dock), без главного окна. Кликаешь по пункту или иконке в Dock, выбираешь длительность и видишь отсчёт рядом с часами. Когда время выходит - заголовок мигает FINISH красным/белым, и ~10 секунд звучит зацикленный сигнал.

Название «WorkFlow» - это просто имя Xcode-проекта. Сама программа - таймер в меню-баре.

Сделано при поддержке TradeCloud. Если бы не TradeCloud, этой программы бы не было.

Возможности

  • Меню-бар + Dock - пункт в строке меню и иконка в Dock; клик по любому открывает таймер (есть и в Cmd+Tab).
  • Пресеты (5 / 10 / 25 / 45 мин) и ручной ввод Ч:М:С.
  • Pause / Resume / Reset через меню во время работы.
  • Оповещение FINISH - заголовок мигает красным/белым (~2 Гц) + зацикленный звук.
  • Устойчивость к сну - остаток считается по дельте Date(), а не по тикам Timer.
  • Ширина не прыгает - моноширинные цифры.
  • Опциональное уведомление через UNUserNotificationCenter.

Состояния

Состояние В меню-баре Клик делает
Idle PRESS ME Открывает popover (пресеты + ввод + START)
Running MM:SS / HH:MM:SS Меню: PAUSE, RESET, Quit
Paused `
Finished мигающий FINISH + звук Любой клик гасит оповещение и возвращает в Idle

Через 10 секунд состояние Finished само возвращается в Idle.

Требования

  • macOS 13 (Ventura) или новее
  • Xcode 15+ (проект в формате синхронизированных папок Xcode 16+)

Сборка и запуск

  1. Открой WorkFlow.xcodeproj в Xcode.
  2. Выбери схему WorkFlow и нажми ⌘R.
  3. Смотри в правую часть строки меню, рядом с часами - там будет PRESS ME.

Главного окна не будет - это правильно. Есть иконка в Dock и пункт в строке меню; клик по любому открывает таймер. Выход - Quit из меню-бара, из попапа или ⌘Q.

Структура проекта

WorkFlow/
├── WorkFlowApp.swift         # точка входа @main, безоконная Settings-сцена + делегат
├── AppDelegate.swift         # связывание компонентов + корректная остановка при выходе
├── TimerEngine.swift         # конечный автомат + логика времени по дельте Date
├── StatusBarController.swift # NSStatusItem: отрисовка, клики, popover, меню
├── TimerPopoverView.swift    # SwiftUI popover: пресеты + Ч:М:С + START
├── AlertPlayer.swift         # зацикленный звук (NSSound)
└── Notifier.swift            # опциональное уведомление

Архитектура: статус-баром владеет AppKit (нужен доступ к attributedTitle с .backgroundColor для мигания и к «сырым» кликам), а контент popover рисует SwiftUI через NSHostingController. TimerEngine не знает про UI - отдаёт только state и коллбэк onUpdate.

Технические детали

  • Устойчивость к сну: отсчёт и 10-секундное окно финиша считаются по дельте Date() - пропущенный во сне тик самокорректируется на следующем.
  • Режим .common: таймеры добавляются как RunLoop.main.add(timer, forMode: .common), поэтому отсчёт обновляется даже при открытом меню.
  • Мигание: заголовок финиша перерисовывается по таймеру 0.25с, чередуя .backgroundColor красный/белый (~2 Гц).
  • Конкурентность: классы помечены @MainActor, таймеры на target/selector - код чист в строгой конкурентности и в режиме Swift 6.

Устранение неполадок

Не вижу пункт в строке меню

Пункт создаётся корректно - он просто скрыт. На MacBook с чёлкой и/или при менеджере меню-бара (Hidden Bar / Bartender / Ice) новый статус-item попадает в скрытую зону за вырезом.

Что делать:

  • Разверни скрытую секцию (стрелка Hidden Bar / Bartender).
  • Зажми ⌘ и перетащи иконки меню-бара - вытащи PRESS ME из-за чёлки в видимую зону.
  • Временно закрой другие менюбар-приложения или переключись на приложение с коротким меню (например, Finder), чтобы бар был свободнее.

Не играет звук финиша

Приложение работает в песочнице. Системные звуки доступны; если NSSound("Funk") недоступен, используется NSSound.beep(). Проверь, что громкость не в mute.

Благодарности

Программа сделана при поддержке TradeCloud. Если бы не TradeCloud, этого проекта бы не было. Спасибо.

Лицензия

MIT © 2026 danila

About

Work flow timer

Resources

Stars

0 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages