# CLAUDE.md This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. ## Project Overview TRMNLc is a self-hosted server that serves rendered displays to [TRMNL](https://usetrmnl.com/) e-ink devices. It renders plugin UIs as HTML using React SSR, screenshots with Puppeteer (Firefox), dithers with ImageMagick for e-ink, and serves via the TRMNL device API. ## Running ```bash bun install # Run the server (requires Firefox and ImageMagick in PATH) FIREFOX=/path/to/firefox CONFIG_FILE=config.json COLORMAP=./colormap.png bun run src/web.ts # Run tests bun test ``` The server runs on port 2300 by default (`BUN_PORT` env var). See `config.sample.json` for the config format. ## Architecture ### Plugin system Each device is configured with a `plugin` name and `settings` object. The plugin registry (`src/plugins.ts`) maps names to factory functions that return a `Renderable`. The `Renderable` interface (`src/template.ts`) requires `hash`, `nextEvent?`, and `async render()`. Available plugins: - **`calendar`** — Full-screen ICS calendar view with current/next/later event slots. UI is in Dutch ("VRIJ", "BEZET", "VOLGENDE"). Implementation: `src/xlcalendar.tsx`. - **`today`** — Daily dashboard with date header, focus/success lines, agenda timeline, chores checklist, and due/overdue tasks. Implementation: `src/todayview.tsx`. ### Data sources (`src/sources/`) The today plugin fetches from external services during `render()`, all in parallel: - **`tana.ts`** — Fetches daily focus/success text and due/overdue tasks from a Tana server. Focus: calendar node → markdown field parsing. Tasks: single search query (`lt` tomorrow), split client-side by due date. - **`ics.ts`** — Fetches today's non-full-day events from ICS feeds, returns `AgendaEvent[]`. Note: `location` lives on `instance.event.location`, not on the expanded instance directly. - **`donetick.ts`** — Fetches chores from a Donetick instance (defaults to `app.donetick.com`). Filters by `user_id` and `isActive`. Done status: `nextDueDate > today` means completed. ### Config structure ```json { "base_url": "http://host:2300", "devices": { "DEVICE_MAC": { "plugin": "today", "settings": { ... }, "refresh": 300, "model": "inkplate_10" } } } ``` ### Render pipeline 1. **`src/web.ts`** — Bun HTTP server. Routes: `/api/setup`, `/api/display` (PNG), `/api/display/html` (raw HTML), `/api/render/:id/:ignore` (cached PNGs). 2. **`src/template.ts`** — Wraps plugin HTML in a full page with TRMNL's CSS/JS framework. 3. **`src/render.ts`** — Puppeteer (Firefox) screenshots the HTML, then ImageMagick dithers to 2-bit grayscale PNG. 4. **`src/devices.ts`** — Fetches device model definitions (screen dimensions, CSS classes) from `trmnl.com/api/models` at startup. ## Deployment Deployed as a NixOS module via `flake.nix` / `default.nix`. The `default.nix` defines a systemd service with `DynamicUser=true`. The Nix derivation copies source directly (no build step — runs with `bun run` at runtime). ## Formatting Uses Prettier: single quotes, trailing commas (es5), 2-space indent, bracket same line.