A two-mode focus timer with a dot-matrix display. Mac menu-bar app + Chrome extension.
mete is a focus timer built around two alternating modes (call them work/rest, deep/shallow, run/walk — whatever fits). Each mode has its own duration, its own visual identity (a shape and a color), and the timer auto-cycles between them. The display is a dot-matrix readout you can click to pause; everything else is one tap or one drag away.
Two surfaces ship in this repo:
- Mac menu-bar app (Electron) — a floating widget that lives near the menu bar, shows up in three sizes, and integrates with system notifications, idle detection, and global hotkeys.
- Chrome extension (Manifest V3) — the same dot-matrix UI as a toolbar popup, backed by a service worker so the cycle keeps ticking even when the popup is closed.
The Mac floater takes three shapes. Right-click the widget or swipe up/down to cycle.
Two more states the Large form moves into:
| Paused | Reminder set |
|---|---|
![]() |
![]() |
Each mode is identified along three axes: shape (primary), color (secondary), and letter (tertiary). Shape stays legible at every size, color helps with quick scanning, the letter is a fallback for accessibility.
Available shapes:
Available colors:
Combined matrix:
Three short badges replace or accompany the profile slot when the timer enters a non-default state:
- pos — post. The cycle target was reached and the timer is now counting overrun. Digits also turn orange.
- ext — extended. Time has been added to the current cycle by rule or by you, on top of the base mode.
- rem — reminder. An independent countdown is running. Tap the badge to cancel.
The slim form replaces the progress ring with the pos badge during overrun:
| Where | Gesture | Result |
|---|---|---|
| Digits | Click | Play / pause |
| Digits | Drag horizontally | Set a reminder duration |
| Digits | Long-press | Open Logs |
| Mode row (Large) | Click | Switch to that mode |
| Skip button | Click | Switch to the other mode |
| Stop button | Click | Pause and reset |
| Anywhere on widget | Right-click | Cycle Large → Pill → Slim |
| Anywhere on widget | Swipe up | Expand to next form |
| Anywhere on widget | Swipe down | Collapse to next form |
Default global hotkeys (Mac, when enabled in Configuration → Core):
| Combo | Action |
|---|---|
| ⌘⌥P | Play / pause |
| ⌘⌥N | Switch to next mode |
| ⌘⌥H | Show / hide floater |
| ⌘⌥F | Toggle form |
Every completed cycle is recorded. The Logs window has two tabs.
Day — per-day totals broken down by mode and cycle count.
Total — aggregates across the whole history. The reset control requires a long-press to confirm.
Three tabs grouping every preference, plus profile selector (P1 / P2 / P3) for switching between full configuration snapshots.
Logic — modes (name, duration, shape, color), behavior (auto-cycle, mode-count display), pre-end warning, idle handling.
View — display theme, digit style, per-mode floater visibility, collapsed form, swipe gesture, blinking colon, digits ghosting.
Core — alerts, sounds, global hotkeys, login launch, time auto-hide.
Toolbar popup version. Service-worker-backed timer so cycles complete and notifications fire even when the popup is closed.
Install:
- Open
chrome://extensions. - Enable Developer mode.
- Load unpacked → select
chrome-ext/. - Pin the extension from the puzzle-piece menu.
Default hotkeys (rebindable in chrome://extensions/shortcuts):
| Combo | Action |
|---|---|
| ⌘⇧P | Play / pause |
| ⌘⇧N | Next mode |
See chrome-ext/README.md for caveats vs. the Mac app.
The Mac menu-bar tray uses a monochrome template image so the system tints it for light or dark menu bars automatically.
npm install
npm run widget # CRA dev server + Electron, hot reload
npm run dist # Build unsigned .dmg + .zip in dist/ (arm64 + x64)
npm run shots # Regenerate the screenshots in docs/images/State persists to disk via electron-store between launches. To reset, delete ~/Library/Application Support/mete/mete.json.
- React 19 + Zustand for the renderer
- Electron with
BrowserWindowresizing per form, separate windows for Logs and Configuration,globalShortcut,Notification,powerMonitoridle polling,app.setLoginItemSettings electron-storefor persistence with alocalStoragefallback in the browser- WebAudio for the click / alert / end / chime tones (no asset files required)
- Manifest V3 for the Chrome extension;
chrome.alarmsfor cycle-end scheduling,chrome.notificationsfor transitions,chrome.storage.localfor state - Puppeteer to regenerate the screenshots in
docs/images/
Inspired by the Interval focus timer at supercommon.systems. This is a from-scratch implementation; no code, assets, or copy were copied from the original.
TBD.















