Uren-assistent is a Tauri desktop app that turns a developer's day — browser history, calendar, GitHub/Linear activity and past bookings — into bookable time blocks in Simplicate. An LLM (Gemini Flash) classifies the day, a deterministic packer lays the result out on a clean timeline, and you book it with one click. Completed weeks are submitted ("ingediend") to Simplicate straight from the app.
activity sources classify place & book submit
┌────────────────────┐ ┌────────────┐ ┌──────────────┐ ┌──────────────┐
│ browser history │ │ Gemini │ │ deterministic│ │ Simplicate │
│ calendar events │ ──────▶│ Flash LLM │ ──────▶│ day-packer │──────▶│ hours + │
│ GitHub / Linear │ │ (1–5 conf) │ │ → timeline │ book │ week submit │
│ past bookings │ └────────────┘ └──────────────┘ └──────────────┘
└────────────────────┘
The in-app workflow is verwerken → boeken → indienen: process a week to generate proposals, book them to Simplicate, then submit the whole week (Mon–Sun) for review.
- 🧠 Day classification — turns your activity into proposed time blocks, each with a 1–5 confidence score.
- 🗓️ Timeline view — a week (Mon–Fri) sidebar plus a per-day timeline with drag-to-book.
- 🔍 Evidence in context — GitHub commits, Linear issues and calendar events shown per day.
- ⏱️ Simplicate integration — read, create, update and delete hours, and submit a week with one click.
- 🔒 Submission status — submitted weeks show as locked / read-only, synced live from Simplicate.
- 📥 CSV history import — bring in browser-history exports for richer classification.
- Tauri 2 (Rust shell) · React 19 · TypeScript · Vite
- Zustand state · Vitest + Testing Library
- Google Gemini Flash (classification) · Google Calendar, GitHub & Linear APIs (context) · Simplicate API (hours)
- Node.js 18+
- Rust (stable) + the Tauri prerequisites for your OS
- A Simplicate environment with an API key/secret, and your own Google/Gemini credentials
Build-time config lives in a local .env (git-ignored — never commit it). Copy the example:
cp .env.example .envVITE_GOOGLE_CLIENT_ID=...
VITE_GOOGLE_CLIENT_SECRET=...
VITE_GEMINI_API_KEY=...
VITE_SIMPLICATE_BASE_URL=https://your-organisation.simplicate.nl/api/v2
⚠️ Security note.VITE_-prefixed values are bundled into the frontend at build time and are therefore present in any distributed binary. Use credentials you control, and never ship a build containing keys you don't want exposed.
Your Simplicate API key + secret are entered in-app and stored in the OS keychain — they are
never written to .env or committed.
npm install
npm run tauri dev # run the desktop app (hot reload)
npm run dev # run the web frontend only (Vite)npm run typecheck # tsc --noEmit
npm run lint # eslint
npm test # vitest
npm run coverage # vitest with coverage (gated at ≥95% on all four metrics)
npm run build # tsc + vite buildThe frontend coverage gate enforces ≥95% statements, branches, functions and lines; the Rust
backend is covered via cargo llvm-cov (cargo cov in src-tauri/).
npm run tauri buildsrc/
├── domain/ # entities, repository interfaces, use cases (no I/O)
├── infrastructure/ # Simplicate, Gemini, Google Calendar, GitHub, Linear, storage
├── application/ # dependency wiring (container.ts)
├── store/ # Zustand app store
└── ui/ # React components, pages, hooks
src-tauri/ # Rust shell + Tauri commands
docs/adr/ # architecture decision records
CONTEXT.md # domain glossary
The codebase follows a clean, layered structure: pure domain logic (use cases over repository interfaces) is isolated from infrastructure (HTTP / keychain / LLM adapters), wired together in application, and consumed by the ui layer. Key decisions are recorded as ADRs, and the domain vocabulary lives in CONTEXT.md.
MIT © Guus Ekkelenkamp