https://github.com/drmowinckels/entracte
Cross-platform break reminder app, named after the theatre interval between acts. macOS-first menubar app built with Tauri 2 + React.
https://github.com/drmowinckels/entracte
break-reminder break-reminder-app desktop-app ergonomics linux macos productivity rust tauri tauri2 typescript windows
Last synced: about 6 hours ago
JSON representation
Cross-platform break reminder app, named after the theatre interval between acts. macOS-first menubar app built with Tauri 2 + React.
- Host: GitHub
- URL: https://github.com/drmowinckels/entracte
- Owner: drmowinckels
- License: apache-2.0
- Created: 2026-05-17T19:11:11.000Z (17 days ago)
- Default Branch: main
- Last Pushed: 2026-05-29T21:58:03.000Z (5 days ago)
- Last Synced: 2026-05-29T22:06:37.448Z (5 days ago)
- Topics: break-reminder, break-reminder-app, desktop-app, ergonomics, linux, macos, productivity, rust, tauri, tauri2, typescript, windows
- Language: Rust
- Homepage: https://drmowinckels.io/entracte/
- Size: 47.2 MB
- Stars: 7
- Watchers: 0
- Forks: 0
- Open Issues: 15
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- Contributing: CONTRIBUTING.md
- License: LICENSE
- Code of conduct: CODE_OF_CONDUCT.md
- Notice: NOTICE
- Agents: .github/AGENTS.md
Awesome Lists containing this project
README
Entracte
Pronounced "ahn-TRAHKT" (French entracte, IPA /ษฬ.tสakt/) โ not "en-tract".
๐ Hear it on the docs site
A cross-platform break reminder app for macOS, Windows, and Linux โ
named after the theatre interval between acts.
Inspired by [Stretchly](https://hovancik.net/stretchly/)
[](https://github.com/drmowinckels/entracte/actions/workflows/ci.yml)
[](https://codecov.io/gh/drmowinckels/entracte)
[](https://github.com/drmowinckels/entracte/releases/latest)
[](LICENSE)

[](https://tauri.app)
[](https://claude.com/claude-code)
---
## What it does
Entracte lives in your menu bar / tray and nudges you to take breaks. It tries hard not to interrupt:
- **Three break kinds** โ short _Micro_ breaks (eye/posture, ~20s), _Long_ breaks (multi-minute), and a _Sleep_ prompt during a configurable bedtime window.
- **Skip when you shouldn't be interrupted** โ pauses for system Do Not Disturb, an active camera (you're in a meeting), idle time (you already stepped away), or hours outside your work window.
- **Pause from the tray** โ 15m / 30m / 1h / 2h / 4h / until tomorrow 6am / indefinitely. The tray icon shows pause bars while paused, so the state is visible at a glance.
- **Multi-monitor aware** โ show breaks on every display, just the primary, or only the monitor your cursor is on, with no Space-hopping fullscreen.
- **Windowed break mode** โ optionally shrink the overlay to 80% of the monitor (centered) so the rest of your desktop stays reachable while the reminder is up.
- **Pre-break heads-up** โ optional notification a configurable number of seconds before each break.
- **Daily screen-time budget** โ opt-in wind-down nudge when you cross a daily active-time budget (default 8 hours), with a configurable snooze interval.
- **Tray countdown** โ optional `MM:SS` countdown next to the menu-bar icon, configurable to track the next short, long, or soonest break (macOS and Linux).
- **Notification-only mode** โ per break kind, swap the overlay for a gentle system notification when you'd rather not be interrupted by a full-screen dim. Note that engagement metrics (completion, skip, postpone) aren't recorded for break types in notification mode, since there's no overlay to act on.
- **Move with you** โ export a full local backup (settings, profiles, break history, screen-time, pause state, manual supporter token if you have one) to JSON and import it on another machine. Atomic stage-then-commit on import; partial-failure rollback restores your previous state.
## Themes
The overlay is always dark โ it has to dim everything else โ but the accent and background tone follow your choice.
## Stats
Entracte keeps a local history of breaks taken, dismissed, and suppressed, with a time-of-day breakdown and a 12-week heatmap. Export to CSV, export/import a full local backup bundle, or clear at any time.
## Install
**macOS** โ via Homebrew (the cask lives in this repo, so tap from URL):
```sh
brew tap drmowinckels/entracte https://github.com/drmowinckels/entracte
brew install --cask drmowinckels/entracte/entracte
```
**Linux / Windows** โ download the `.deb`, `.rpm`, `.AppImage`, `.msi`, or `.exe` from the [Releases page](https://github.com/drmowinckels/entracte/releases) (pick the latest tag; pre-releases โ the `0.0.X` beta line โ don't surface under `releases/latest`).
> **Windows users:** the `.msi` / `.exe` aren't code-signed yet โ SignPath Foundation turned down our first application on visibility grounds (the project is too new). SmartScreen will warn you when you run the installer; click **More info โ Run anyway** to proceed. See [the install guide](https://entracte.drmowinckels.io/guide/install#windows) for how you can [help us get there](https://entracte.drmowinckels.io/guide/install#help-us-get-windows-signed) โ stars, forks, mentions, and contributions all count.
## Command line
The same `entracte` binary doubles as a small CLI for scripting / hotkey wiring. Action commands forward to the running tray app:
```sh
entracte pause 30m # pause for 30 minutes
entracte trigger long # fire a long break now
entracte --profile=Focus --colour=midnight # switch profile + theme in one call
entracte status # JSON: pause state + active profile
entracte log # tail the entracte log
entracte help # full reference
```
Full command reference, IPC details, and tips in [docs/guide/cli.md](docs/guide/cli.md).
## Free and open
Entracte is free, cross-platform, and open source under Apache 2.0. Every scheduling, suppression, profile, hooks, stats, accessibility, and CLI feature is available to everyone.
A **[Supporter pack](docs/guide/supporter.md)** is available as a way to back development. It unlocks personalisation extras โ custom overlay colour, theme rotation, editable break hints, custom CSS, and custom sounds โ through a one-off purchase. The unlock check lives in plain source: it's an honour-system thank-you, not a DRM scheme. Nothing in the scheduling, suppression, profile, hooks, stats, accessibility, or CLI surface is gated.
## Stack
- React 19 + TypeScript + Vite frontend.
- Rust + [Tauri 2](https://tauri.app) + Tokio backend.
- Per-OS native hooks for Do Not Disturb, camera-in-use, and idle detection.
## Architecture
The scheduler is the brain: a Tokio tick loop in [src-tauri/src/scheduler/](src-tauri/src/scheduler/) that consults native hooks, applies pause/suppress rules, and decides when (and how) to surface a break. The tray, CLI, and settings UI all push into the same scheduler; the overlay and stats are downstream of its decisions.
```mermaid
flowchart LR
subgraph UI["User surface"]
Tray["Tray icon + menu"]
CLI["entracte CLI"]
SettingsUI["Settings UI
(React)"]
Overlay["Break overlay
(React)"]
SysNotification["System notification"]
end
subgraph Backend["Rust / Tauri backend"]
IPC["ipc.rs
CLI โ app bridge"]
Scheduler["scheduler/
tick loop"]
Hooks["Native hooks
DnD ยท camera ยท idle ยท session lock"]
end
subgraph Stores["On-disk state"]
Config[("settings.json
profiles + settings")]
Stats[("events.jsonl
break history")]
Pause[("pause.json")]
end
CLI -->|subcommands| IPC
Tray -->|menu actions| Scheduler
IPC --> Scheduler
SettingsUI <-->|tauri invoke| Scheduler
Hooks -->|suppress signals| Scheduler
Scheduler -->|fires break| Overlay
Scheduler -->|notify-only mode| SysNotification
Scheduler <--> Config
Scheduler --> Stats
Scheduler <--> Pause
```
Platform support matrix, scheduler internals, and OS-specific quirks are documented in [.github/AGENTS.md](.github/AGENTS.md).
## Development
```sh
npm install
npm run tauri dev # full app, hot reload on TS + Rust
npm test # vitest, frontend unit tests
npm run audit:a11y # build + headless Chromium + axe-core on every tab ร light/dark
cargo test --manifest-path src-tauri/Cargo.toml --lib
```
The a11y audit ([scripts/audit-a11y.mjs](scripts/audit-a11y.mjs)) builds `dist/`, serves it via `vite preview`, drives Chromium through Puppeteer with a tiny `__TAURI_INTERNALS__` shim so the React tree renders normally, then runs [axe-core](https://github.com/dequelabs/axe-core) on every tab in both `prefers-color-scheme` modes. It runs on every CI build via [.github/workflows/ci.yml](.github/workflows/ci.yml) and exits non-zero on any WCAG 2.1 AA violation.
Platform support matrix, scheduler internals, and OS-specific quirks are documented in [.github/AGENTS.md](.github/AGENTS.md).
## Updates
Updates ship via [`tauri-plugin-updater`](https://v2.tauri.app/plugin/updater) against GitHub Releases. The About tab calls `check_for_update`, which delegates to the plugin: it fetches the signed `latest.json` manifest at `releases/latest/download/latest.json`, verifies the bundled signature against the public key pinned in `tauri.conf.json`, and reports whether a newer version is announced. macOS (Apple-signed + Tauri-signed), Linux AppImages (Tauri-signed), and Windows `.msi` (Tauri-signed) are all in `latest.json`. The check is manual โ clicking **Check for updates** opens the release page in your browser; no automatic check on app start, no silent download_and_install. `.deb` / `.rpm` users update through their system package manager and are deliberately outside the updater flow.
> **Beta caveat.** `releases/latest` only resolves to stable releases. While we're on the `0.0.X` beta line (no stable yet), the in-app **Check for updates** returns _no update available_ even when a newer beta has shipped. Beta-to-beta upgrades are manual โ watch the [Releases page](https://github.com/drmowinckels/entracte/releases) or `brew upgrade --cask entracte` if you installed via the tap. Once `0.1.0` ships and is published as a non-prerelease, the in-app check starts working for everyone.
Windows specifically: until [SignPath Foundation](https://signpath.org) approves the project, the `.msi` is unsigned-via-Authenticode and SmartScreen warns every install. The About tab calls this out next to the "Open release page" link so users know to click **More info โ Run anyway**.
**Updater signing key.** The pinned public key in [`src-tauri/tauri.conf.json`](src-tauri/tauri.conf.json) (`plugins.updater.pubkey`) is the trust root every installed copy uses to verify the `latest.json` manifest. Its matching private key lives in two GitHub repo secrets โ `TAURI_SIGNING_PRIVATE_KEY` (base64 contents of the key file) and `TAURI_SIGNING_PRIVATE_KEY_PASSWORD` (passphrase) โ both wired into the `build-unix` job in [`.github/workflows/release.yml`](.github/workflows/release.yml). The release workflow signs each macOS `.app.tar.gz` with that key and composes the `latest.json` manifest from both arches in the `publish-updater-manifest` job; a pre-flight `verify-updater-pubkey` job refuses any tag whose pubkey is still the bring-up placeholder.
**Rotating the key** breaks auto-update for every installed copy on the old pubkey โ they can't validate signatures from the new one and silently fall behind. Rotate only when you have a path to manual reinstall for affected users (e.g. a security incident). To rotate: `tauri signer generate -w ~/.tauri/entracte.key`, paste the new public key into `tauri.conf.json`, update both `TAURI_SIGNING_*` GitHub secrets, ship a release that users must install manually, then any later release will auto-update normally.
## Contributing
Bug reports, ideas, and patches are all welcome. Start with [CONTRIBUTING.md](CONTRIBUTING.md) for the setup, test, and PR workflow. Participation is governed by the [Code of Conduct](CODE_OF_CONDUCT.md), which โ among the usual things โ requires a real human reviewer in the loop on every contribution.
## Status
Functional and usable day-to-day on macOS. Windows and Linux build and run; some detection features (Linux DnD, Wayland idle) are still gapped โ see [AGENTS.md](.github/AGENTS.md#known-gaps--next-moves).
Settings persist to a JSON file in the OS app-config dir:
- **macOS** โ `~/Library/Application Support/io.drmowinckels.entracte/`
- **Windows** โ `%APPDATA%\io.drmowinckels.entracte\`
- **Linux** โ `~/.config/io.drmowinckels.entracte/`