Implements an e-ink daily dashboard plugin ("today") with four sections:
date/focus/success header, agenda timeline, chores checklist, and
due/overdue task lists.
Data sources:
- Focus & success text: Tana daily note (src/sources/tana.ts)
- Due/overdue tasks: Tana task search (src/sources/tana.ts)
- Agenda events: ICS calendar feeds (src/sources/ics.ts)
- Chores: Donetick API (src/sources/donetick.ts)
All sources fetch in parallel and fall back gracefully on error.
Tests use mock HTTP servers with synthetic data — no real services needed.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
3.1 KiB
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 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
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 (lttomorrow), split client-side by due date.ics.ts— Fetches today's non-full-day events from ICS feeds, returnsAgendaEvent[]. Note:locationlives oninstance.event.location, not on the expanded instance directly.donetick.ts— Fetches chores from a Donetick instance (defaults toapp.donetick.com). Filters byuser_idandisActive. Done status:nextDueDate > todaymeans completed.
Config structure
{
"base_url": "http://host:2300",
"devices": {
"DEVICE_MAC": {
"plugin": "today",
"settings": { ... },
"refresh": 300,
"model": "inkplate_10"
}
}
}
Render pipeline
src/web.ts— Bun HTTP server. Routes:/api/setup,/api/display(PNG),/api/display/html(raw HTML),/api/render/:id/:ignore(cached PNGs).src/template.ts— Wraps plugin HTML in a full page with TRMNL's CSS/JS framework.src/render.ts— Puppeteer (Firefox) screenshots the HTML, then ImageMagick dithers to 2-bit grayscale PNG.src/devices.ts— Fetches device model definitions (screen dimensions, CSS classes) fromtrmnl.com/api/modelsat 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.