stuarth/focus-cycle

★ 0Forks 0SwiftGitHub ↗Compare

README

Focus Cycle

A native Mac menu bar app that makes distracting sites less convenient to open. Focus and break intervals repeat automatically until you stop the cycle.

Serious effort. Silly bicycle. A playful training partner for doing the work, taking a breather, and showing up again. See the brand voice.

Build and run

Requires macOS 14 or newer and a Swift 6 toolchain (Xcode or Command Line Tools).

swift test
./scripts/build-app.sh
open "dist/Focus Cycle.app"

The build produces an ad-hoc signed local app. You can move it to ~/Applications. It is not notarized for distribution to other Macs.

Use

  1. Open Focus Cycle from the menu bar and configure your intervals, blocked sites, and apps to hide.
  2. Start a cycle. Defaults are 25 minutes of focus and 5 minutes of break.
  3. Authorize website blocking when macOS asks. This happens at the first blocked focus interval after launching the app, not on every interval.
  4. During focus, listed sites are blocked and selected running apps are hidden. You can reopen hidden apps normally.
  5. During breaks, sites become available. The next focus interval starts automatically.

Take a breather during focus requires a 30-second wait and confirmation, then starts your configured break and resumes with a fresh focus interval. Finish for today ends the loop. Stopping or quitting during focus requires a 30-second wait followed by confirmation. You can cancel that request and keep focusing. Stopping during a break is immediate. Settings changes apply to the next manually started cycle. Settings open inside the menu bar panel. Back discards unsaved edits; Save settings returns to the timer with confirmation.

Training log

Open Training log from the timer to see today's focus minutes against your daily target, target percentage, full focus intervals, manual break count and duration, and a timeline. Set your Daily focus target in settings; it starts at 150 minutes and updates today's score when changed.

Completed focus is green, interrupted focus and manual breaks are amber, and a missed daily target turns red after finishing for today or reaching your workday end. Planned recovery and gaps stay neutral. Partial focus still contributes to total time. There are no coaching messages in the log.

History is saved locally in ~/Library/Application Support/Focus Cycle/training-log.json. It records only intervals, timestamps, and completion status. Sleep and screen lock pause focus credit; a break already started continues to its deadline. A relaunch closes an unfinished segment at its last saved checkpoint instead of crediting the time while the app was closed. Checkpoints are saved every 15 seconds and at transitions. Existing history is preserved if it cannot be read; the log displays an error rather than replacing it.

Workday schedule

In settings, enable Use a daily schedule, choose your days, and set Start after and End at. Scheduling starts disabled, with Monday–Friday, 9 am–5 pm as editable defaults. Times follow your Mac's local timezone; the end must be later than the start on the same day.

The first keyboard, mouse, or tablet activity after the start time begins a cycle. Focus and break repeat until the end time, when the cycle stops and website access is restored. If you're away, it waits for fresh activity; returning after the end time doesn't start anything. It also waits while you're editing settings.

There is one automatic attempt per local day, remembered across app restarts. Stopping early, skipping today's schedule, or cancelling administrator approval doesn't cause another automatic attempt that day. You can always start manually; manual starts count for today's attempt and continue until you stop them. A running scheduled cycle keeps its original end time if you later edit or disable the schedule.

Focus Cycle must be running. Enable Open at login to make that easier; macOS may require you to allow it in Login Items. It opens quietly in the menu bar at login. The first website block after each app launch still requires administrator approval. Scheduling does not install a permanently privileged service.

Activity detection reads elapsed idle time, not keystrokes or event contents. It suspends around sleep, screen lock, and switching users. Lock detection also uses macOS lock notifications and a session flag that Apple doesn't publicly document; this should be rechecked on new macOS versions.

The site list is imported from ~/.focus on first use when that file exists. Slack and Discord are the initial apps to hide. Focus Cycle does not modify your old CLI or its configuration.

macOS Focus mode

Focus Cycle can run Apple Shortcuts to turn a chosen macOS Focus mode on during focus intervals and off during both kinds of break, when you finish, and when you quit. This integration starts disabled.

One-time setup in Apple's Shortcuts app:

  1. Create a shortcut named Focus Cycle On with one Set Focus action. Choose your mode (for example, Work) and set it On until turned off.
  2. Create Focus Cycle Off with Set Focus for that same mode, set to Off.
  3. Run each once in Shortcuts to approve any first-use prompts. Use explicit On and Off actions, not Toggle, and avoid actions that wait for input.
  4. In Focus Cycle settings, enable Set Focus automatically. Refresh the list, choose those two shortcuts, and save.

The mode is selected inside the shortcuts. Focus Cycle saves their identifiers, so renaming a shortcut does not break the selection. Setting changes apply to the next manually or automatically started cycle. The Off shortcut turns the chosen mode off; it does not restore a mode that was active before the session.

Shortcut errors appear in the timer panel. Website access is restored independently of shortcut success, and failed Off actions can be retried there. An outstanding Off action is remembered across relaunches, even if you change or disable the integration. A force quit cannot turn the mode off immediately; reopening Focus Cycle retries the remembered Off action. Commands time out after ten seconds; keep these shortcuts to the single Set Focus action, since terminating the CLI cannot guarantee cancellation of actions already executing inside Shortcuts.

The app uses Apple's documented Shortcuts command-line interface.

Website blocking

The app bundles a small administrator-authorized helper that manages only its own marked section in /etc/hosts. It preserves unrelated entries, maps listed domains and their www variants for both IPv4 and IPv6, and flushes the system DNS cache after changes. List any other subdomains explicitly.

This is friction, not a tamper-proof network filter. Existing browser connections can remain usable until they reconnect. Proxies and some browser/network configurations may bypass local hosts-based resolution.

The helper uses a renewable lease so blocking expires when the app stops responding. Normal quit removes its entries immediately. The helper remains available during breaks and exits when the app exits. Nothing is installed as a launch daemon. If a restart or a forced helper shutdown leaves a block behind, reopen the app and choose Restore website access. Recovery may request administrator authorization. Failed recovery stays visible and never loops through authorization prompts.

If the old focus CLI still has an active # FOCUS section in /etc/hosts, Focus Cycle reports the conflict instead of silently taking ownership. Remove that old block before starting. The old CLI's disable implementation truncates everything from its marker onward, so inspect and preserve any later unrelated entries before using it.

Development

swift test
./scripts/build-app.sh debug
open "dist/Focus Cycle.app" --args --preview

Preview opens the interface without starting a focus interval or modifying hosts. Automated tests use temporary data and do not require administrator privileges.

  • Sources/FocusCycleApp: menu bar interface, settings, and coordination.
  • Sources/FocusCycleCore: deterministic timer and persisted configuration.
  • Sources/FocusCycleBlocking: domain validation, hosts editing, and helper client.
  • Sources/FocusCycleHelper: privileged helper entry point.

See the v1 scope for the agreed behavior and implementation defaults.

Contributors

stuarth

Issues