Skip to content

byuly/sbox

Repository files navigation

sbox

A native macOS study session app that puts every tool you need — timer, notes, tasks, music, ambient sound, and calendar — in one persistent window. No alt-tabbing, no lost focus.

Platform: macOS 13+


Features

  • Pomodoro timer — focus/break cycles with macOS notifications
  • Tasks — lightweight to-do list with tags
  • Notes — markdown editor backed by .md files on disk
  • Spotify — remote control your Spotify playback via the Web API (Premium required)
  • Ambient sound — mix rain, cafe, fireplace, and white noise
  • Calendar — import .ics files and view your agenda

Architecture

graph TD
    subgraph Frontend ["Frontend (React + TypeScript)"]
        UI[Panels: Strip / Tasks / Notes / Calendar]
        Stores[Zustand Stores]
        Hooks[Event Hooks]
        UI --> Stores
        Hooks --> Stores
    end

    subgraph IPC ["Tauri IPC Boundary"]
        Invoke["invoke() — commands"]
        Listen["listen() — events"]
    end

    subgraph Backend ["Backend (Rust)"]
        Pomodoro[pomodoro\nstate machine + tick loop]
        Tasks[tasks\nCRUD]
        Notes[notes\nfile I/O]
        Spotify[spotify\nOAuth PKCE + Web API]
        Ambient[ambient\nrodio audio mixer]
        Calendar[calendar\n.ics parser]
        Prefs[preferences\nkey-value store]
        DB[(SQLite\nsbox.db)]
        FS[(Disk\nnotes/*.md\ntokens.json)]
    end

    Stores -->|invoke| Invoke
    Invoke --> Pomodoro & Tasks & Notes & Spotify & Ambient & Calendar & Prefs
    Pomodoro & Tasks & Notes & Spotify & Ambient & Calendar & Prefs -->|emit events| Listen
    Listen --> Hooks
    Tasks & Calendar & Prefs --> DB
    Notes --> FS
    Spotify -->|tokens| FS
Loading

Key patterns

  • Domain layout — every backend domain follows a 4-file shape: mod.rs (commands), state.rs (types), service.rs (logic), store.rs (persistence). Domains don't cross into each other's internals.
  • IPC — frontend calls invoke() for commands and listen() for push events. Zustand stores update optimistically from command return values; events are a safety net.
  • Ambient audiorodio is not Send + Sync, so audio runs on a dedicated std::thread that owns all sinks and communicates via a channel.
  • SQLite — WAL mode, single Mutex<Connection> shared across tokio tasks.

Data locations

Data Location
Tasks, Calendar, Preferences ~/Library/Application Support/sbox/sbox.db
Notes ~/Library/Application Support/sbox/notes/*.md
Spotify tokens ~/Library/Application Support/sbox/tokens.json
Ambient audio Bundled in .app/Contents/Resources/audio/

Getting started

Prerequisites

Install dependencies

npm install

Run in development

npm run tauri dev

Build for production

npm run tauri build

The .dmg and .app will be in src-tauri/target/release/bundle/.

Spotify setup

sbox uses Spotify's Web API with PKCE — no client secret is stored. To connect:

  1. Create an app at developer.spotify.com
  2. Add http://127.0.0.1:8888/callback as a redirect URI
  3. Put your Client ID in the app config
  4. Click "Connect Spotify" in the app and log in

Spotify Premium is required for playback control.


Stack

Layer Technology
Desktop shell Tauri 2
Backend Rust, Tokio, rusqlite, reqwest, rodio
Frontend React 19, TypeScript, Tailwind CSS v4
State Zustand
Editor Tiptap (ProseMirror)
Build Vite

About

one box for studying. has all you need :)

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors