A permanent desktop dashboard for macOS, pinned to the right edge of your widescreen monitor.
How it works · Widgets · Setup · Customization
Widescreen monitors have too much horizontal space. Most of it sits empty while you work in the center. This project turns that dead zone into a glanceable dashboard with everything you care about: calendar, tasks, music, system stats, clipboard history, recent files.
It's always visible, never steals focus, and disappears automatically when you undock your laptop.
The sidebar is a Svelte + Tailwind app running inside a Hammerspoon webview pinned to the right 300px of your external display.
┌───────────────────────────────────┐
│ Your monitor │
│ │
│ ┌───────────────────┐ ┌───────┐ │
│ │ App windows │ │Sidebar│ │
│ │ (2260px usable) │ │(300px)│ │
│ │ │ │ │ │
│ └───────────────────┘ └───────┘ │
└───────────────────────────────────┘
- Hammerspoon creates a borderless, floating, non-activating webview (you can click through it, it never steals focus)
hs.screen.watcherdetects dock/undock and auto-hides the sidebar when your external monitor disconnects- Window management via CapsLock (Hyper key) + arrow keys is sidebar-aware: splits only use the 2260px outside the sidebar
- Two-way communication: Svelte talks to Lua via
webkit.messageHandlers, Lua pushes data back viaevaluateJavaScript()
- Dev: Hammerspoon webview points at
http://localhost:5174(Vite dev server with HMR) - Prod:
bun run build, then Hammerspoon loadsfile:///path/to/dist/index.htmldirectly
| Widget | Data source | Update interval |
|---|---|---|
| Calendar | Static (date + mini month grid) | On load |
| Todoist | Todoist API v1 (direct fetch) | 2 min |
| Now Playing | hs.spotify |
5s |
| Clipboard | hs.pasteboard |
2s |
| Screenshots | ~/Desktop/Screenshot*.png |
30s |
| Downloads | ~/Downloads (recent files) |
30s |
| Quick Note | Saves to Obsidian vault via Lua | On submit |
| System Stats | hs.host.cpuUsage, hs.host.vmStat, df, hs.battery |
10s |
| Top Processes | ps aux |
10s |
- macOS with an external monitor
- Hammerspoon (
brew install --cask hammerspoon) - Karabiner-Elements with CapsLock mapped to Hyper (ctrl+alt+cmd+shift)
- Bun
- Accessibility permissions for Hammerspoon (System Settings > Privacy & Security > Accessibility)
git clone https://github.com/sasha-computer/sidebar.git
cd sidebar
bun installEdit hammerspoon/sidebar.lua:
SIDEBAR_WIDTH(default: 300)EXTERNAL_DISPLAY(default: "LG UltraFine") - change to match your monitor nameNOTES_PATH- path to your notes directoryUSE_DEV-truefor dev server,falsefor production build
Edit hammerspoon/init.lua:
- The Todoist token is read from 1Password at startup. Replace the
io.popenline with your own token source, or hardcode it (not recommended).
ln -sf "$(pwd)/hammerspoon/init.lua" ~/.hammerspoon/init.luabun run dev # Start Vite on :5174
# Hammerspoon auto-reloads when .lua files changebun run build # Outputs to dist/
# Set USE_DEV = false in sidebar.lua
# Reload HammerspoonCapsLock is remapped to Hyper (ctrl+alt+cmd+shift) via Karabiner. Hammerspoon binds:
| Shortcut | Action |
|---|---|
| Hyper + Left | Left half (sidebar-aware) |
| Hyper + Right | Right half (sidebar-aware) |
| Hyper + Up | Maximize (sidebar-aware) |
| Hyper + Down | Center 60% |
| Hyper + Return | Toggle fullscreen |
| Hyper + S | Toggle sidebar |
When undocked (no external monitor), splits use the full screen width.
- Create
src/widgets/MyWidget.svelte - Add a store in
src/lib/stores.tsif needed - Import and add it to
src/App.svelte - If the widget needs macOS data, add a collector function in
hammerspoon/sidebar.luaand push data viapushToWebview()
Colors are defined in src/app.css using Catppuccin Mocha. Change the @theme block to use your palette.
Change EXTERNAL_DISPLAY in sidebar.lua to match your monitor's name. Find yours with:
hs -c 'for _, s in ipairs(hs.screen.allScreens()) do print(s:name()) end'- UI: Svelte 5 + Tailwind v4 + Lucide icons
- Host: Hammerspoon
hs.webview(WKWebView) - Bundler: Vite (via Bun)
- Font: Berkeley Mono
- Theme: Catppuccin Mocha
MIT

