A macOS menu bar app built with Tauri v2 that wraps the Clocktopus dashboard. It provides quick access to start/stop timers from the system tray with a live status indicator.
-
Download the latest
.dmgfrom GitHub Releases -
Open the DMG and drag Clocktopus.app to
/Applications -
Important: The app is not code-signed. macOS will block it on first launch with "app is damaged". Run this in Terminal to fix:
xattr -cr /Applications/Clocktopus.app
-
Open Clocktopus from Applications
-
The dashboard server must be running for the app to work:
clocktopus serve
desktop/
├── src-tauri/
│ ├── src/lib.rs # Main app logic (tray icon, window, timer polling)
│ ├── Cargo.toml # Rust dependencies
│ ├── tauri.conf.json # App config (window size, permissions, bundling)
│ ├── capabilities/ # Security permissions (opener plugin, etc.)
│ ├── icons/ # App and tray icons (generated)
│ └── build.rs # Tauri build script (auto-generated, don't edit)
└── package.json # Dev scripts (bun run dev / bun run build)
The desktop app is a thin shell — it does not contain any business logic. It loads the dashboard UI from http://localhost:4001 and adds:
- A system tray icon (octopus with green status dot when timer is active)
- Click to toggle the dashboard window
- Right-click menu with "Open Dashboard" and "Quit"
- Close to tray — closing the window hides it instead of quitting
- Timer status polling — checks
/api/timer/activeevery 5 seconds to update the tray icon
Install via rustup:
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | shChoose option 1 (default). Restart your terminal or run:
source "$HOME/.cargo/env"Verify:
rustc --version # should print 1.77+
cargo --versionxcode-select --installbun --versionFrom the desktop/ directory:
cd desktop
bun installThis installs the @tauri-apps/cli dev dependency. Rust dependencies are fetched automatically on first build.
Important: The dashboard server must be running before launching the desktop app.
# From the project root
bun run dashboardcd desktop
bun run devThe first build takes 3-5 minutes (compiling Rust dependencies). Subsequent builds are fast (~1-2 seconds) thanks to incremental compilation.
Hot reload:
- Rust changes (
src-tauri/src/): Tauri CLI detects changes and recompiles automatically - Frontend changes (
dashboard/views.ts): Rebuild the main project (bun run buildin root), then refresh the window
cd desktop
bun run buildOutput:
src-tauri/target/release/bundle/macos/Clocktopus.app # Standalone app
src-tauri/target/release/bundle/dmg/Clocktopus_0.1.0_aarch64.dmg # Installer
Drag Clocktopus.app to /Applications, or open the .dmg and drag from there.
Note: The dashboard server (bun run dashboard) must still be running for the app to work. The desktop app connects to http://localhost:4001.
Create a 1024x1024 PNG logo with transparent background (RGBA, not RGB), then run:
cd desktop
bunx tauri icon path/to/your-logo-1024x1024.pngImportant: The source PNG must have an alpha channel (transparent background). If exporting from Affinity Designer/Photo, ensure the background layer is hidden and export as PNG with transparency enabled. You can verify with:
sips -g hasAlpha path/to/your-logo.png
# Should show: hasAlpha: yesThis generates all required sizes in src-tauri/icons/. After regenerating, rebuild the app:
cd desktop
bun run build| File | Size | Purpose |
|---|---|---|
icon.icns |
Multi-size | macOS app icon (.app, .dmg, Dock) |
32x32.png |
32x32 | Also used as the menu bar tray icon |
The tray icon uses src-tauri/icons/32x32.png (your logo scaled down). When a timer is active, a green status dot is overlaid in the bottom-right corner. This is handled in src-tauri/src/lib.rs.
## Configuration
### tauri.conf.json
Key settings in `src-tauri/tauri.conf.json`:
```jsonc
{
"app": {
"withGlobalTauri": true, // Exposes window.__TAURI__ to frontend JS
"windows": [
{
"title": "Clocktopus",
"width": 420, // Window dimensions
"height": 600,
"resizable": true,
},
],
},
"build": {
"frontendDist": "http://localhost:4001", // Dashboard URL
"devUrl": "http://localhost:4001",
},
}
src-tauri/capabilities/default.json controls what the app can do:
core:default— basic window managementopener:default— open URLs in system browser (used for OAuth flows)
| Crate | Purpose |
|---|---|
tauri |
Core framework (with tray-icon, image-png features) |
tauri-plugin-opener |
Open external URLs in system browser |
reqwest |
HTTP client for polling timer status |
- On launch, a system tray icon is created with the octopus shape
- A background thread polls
GET /api/timer/activeevery 5 seconds - When timer state changes, the icon swaps between:
- Idle: white octopus, no dot
- Active: white octopus + green dot (bottom-right corner)
- Tooltip updates to "Clocktopus - Timer running" when active
- Left-click tray icon: toggles window visibility
- Right-click tray icon: context menu (Open Dashboard / Quit)
- Close button (red X): hides window to tray instead of quitting
- Window loads
http://localhost:4001(same as opening in a browser)
The dashboard detects when running inside Tauri (window.__TAURI__) and opens OAuth URLs in the system browser via the opener plugin, instead of navigating away from the dashboard.
The dashboard server isn't running. Start it:
bun run dashboardKill all instances and restart:
pkill -f "target/debug/app" && pkill -f "target/release/Clocktopus"
cd desktop && bun run devNormal — Rust compiles ~400 crates on the first build. Subsequent builds only recompile changed code.
When opening the built .app, macOS may block it. Right-click > Open > Open to bypass, or sign it:
codesign --force --deep --sign - src-tauri/target/release/bundle/macos/Clocktopus.appFor distribution, you'll need an Apple Developer certificate.
Check that the dashboard server is reachable at http://localhost:4001/api/timer/active. The poller silently retries on failure.