Imported from sunyxedu/focusd (
AGENTS.md). Install upstream withnpx skills add sunyxedu/focusd. Copyright stays with the author.
Working in this repo
Focusd: task management and planning that runs on macOS, Windows, Linux, iOS, iPadOS and the web from one codebase.
core/ Rust — ALL behaviour (model, availability/blocking, perspectives,
repeats, review, natural-language dates, undo, persistence, JSON
bridge, wasm export). #![deny(unsafe_code)].
app/ React + TypeScript UI (Vite). Renders Snapshots from the core.
desktop/ Tauri 2 host: native window/menu, on-disk database, calendar
feeds (ICS), Mail Drop (IMAP), notifications, deep links.
#![forbid(unsafe_code)]. Also the iOS/Android host (gen/).
scripts/ build-wasm.sh — core → app/wasm for the web build.
Rules
- No
unsafein Rust. The only exception iscore/src/wasm.rs, where the#[wasm_bindgen]macro itself expands to FFI glue. - Behaviour lives in
core/, never in TypeScript. The UI callsstore.api.*(app/src/core/api.ts → core/src/bridge.rs) and renders the returnedSnapshot. If the UI needs data it cannot get, add it to the core and toapp/src/core/types.ts(camelCase mirror of the Rust types). - Presentation-only helpers (date formatting) live in
app/src/core/format.ts; date parsing isapi.parseDate. - One commit per step; keep
cargo test,cargo clippy -D warnings,npm run typecheckgreen. - Screenshot with
app/tools/shot.mjsand compare against the current desktop look before calling visual work done.
Commands
cargo test -p focusd_core # core behaviour tests
cargo clippy -p focusd_core -- -D warnings
./scripts/build-wasm.sh # needs wasm-bindgen-cli
cd app && npm install && npm run dev # web build at :5180 (wasm core)
cd app && npm run typecheck && npm run build
cd desktop && cargo tauri dev # native desktop (native core)
cd desktop && cargo tauri build # .app/.dmg, .msi, .deb/.AppImage
cd desktop && cargo tauri ios dev "iPhone 18 Pro" # needs full Xcode selected
node app/tools/shot.mjs out.png http://localhost:5180/ 1500 980 "<js>"
Conventions
- Core timestamps: i64 milliseconds, local wall-clock (chrono + Local).
- JSON bridge:
dispatch(store, method, args); args keyed by parameter name; tri-state args use{ "v": x }(seetri()in bridge.rs). after: ""in move methods means "insert as first sibling".- Serde: every exported struct/enum is
rename_all = "camelCase"; row enums are internally tagged withkind. - UI layouts:
body[data-layout]= compact (<700px, phone) | regular (<1000px, tablet) | wide (Mac). Dark mode:body.darkviaapp/src/core/appearance.tsandapp/src/styles/dark.css.