Add CLAUDE.md and docs/widgets.md; fix stale single-mode architecture docs
Repo-tracked context so a fresh Claude Code session (this machine or a remote/cloud one) gets accurate project context without relying on this session's local, machine-specific memory: repo conventions (no co-author trailers, flag copyleft deps explicitly, scope security gates broadly -- each backed by a real past incident), testing/deploy workflow, and pointers into the existing docs. docs/architecture.md's sequence diagram and boot-flow text still described the pre-widget-system single-photo-queue model (e.g. "force- advance to next queued photo") even though that was fully replaced by the widget system across this branch's recent history -- fixed, and added docs/widgets.md distilling the widget system's actual design (data model, grid placement, compositor, button-action dispatch) as current-state documentation, including the still-open Phase 6 cleanup (legacy Frame columns not yet dropped) as a known gap.
This commit is contained in:
@@ -0,0 +1,71 @@
|
||||
# espresso_frame
|
||||
|
||||
A DIY e-ink photo frame: an ESP32-C6 (`firmware/`, ESP-IDF) driving a
|
||||
Waveshare 7.3" E Ink Spectra 6 panel (800x480, 6-color, SPI), paired with a
|
||||
self-hosted FastAPI server (`server/`) that pulls from Immich, does all
|
||||
image processing (crop/dither/quantize/pack), and serves a placeable
|
||||
photos/calendar/whiteboard widget system to the device.
|
||||
|
||||
Start here, don't re-derive from scratch:
|
||||
- [`docs/architecture.md`](docs/architecture.md) -- how firmware and
|
||||
server talk (sequence diagram, boot flow).
|
||||
- [`docs/widgets.md`](docs/widgets.md) -- the server-side widget system
|
||||
(data model, grid placement, compositor, button-action dispatch). Notes
|
||||
a known gap at the bottom (legacy `Frame` columns not yet dropped).
|
||||
- [`docs/hardware.md`](docs/hardware.md) -- wiring.
|
||||
- [`server/README.md`](server/README.md), [`firmware/README.md`](firmware/README.md)
|
||||
-- per-component setup, config, and a lot of accumulated gotchas
|
||||
(Immich API shape, TLS trust-anchor details, button GPIO wakeup
|
||||
quirks, etc.) -- check these before assuming something is a new bug.
|
||||
|
||||
## Conventions specific to this repo
|
||||
|
||||
- **No `Co-Authored-By: Claude` trailers in commits.** Attribution lives
|
||||
in the root [`README.md`](README.md) instead (see its last line) --
|
||||
the maintainer's explicit preference, not the default.
|
||||
- **Copyleft dependencies need an explicit flag, not a silent decision.**
|
||||
Before adding anything LGPL/GPL/AGPL (or unclear), verify the actual
|
||||
license via `pip show`/package metadata -- including transitive deps,
|
||||
not just the top-level package -- and present the finding and tradeoff
|
||||
in plain text rather than picking an approach unilaterally (hand-rolling
|
||||
an alternative, swapping packages, silently accepting it). This project
|
||||
has knowingly accepted AGPL-3.0-or-later exposure once already
|
||||
(`icalendar-searcher`, a transitive dep of `caldav`) as a deliberate,
|
||||
explicit call -- not a precedent for skipping the check next time.
|
||||
- **Scope new auth/access-control broadly, not just to the literal
|
||||
endpoint named.** When a request changes the trust model (e.g. adding
|
||||
public-internet exposure), apply the new gate to every endpoint serving
|
||||
real data or performing a real action, and call out anything you're
|
||||
tempted to exclude and why. This repo shipped a token gate once that
|
||||
covered `/api/*` but left `/frame/image` -- the actual photo bytes --
|
||||
open; caught immediately in production.
|
||||
|
||||
## Working in this repo
|
||||
|
||||
- **Server tests**: `cd server && pytest` (SQLite, fixtures wipe/reseed
|
||||
between tests -- see `tests/conftest.py`). Migration changes need a
|
||||
matching test in `tests/test_migrations.py`; anything touching
|
||||
`require_frame_view`/`require_frame_control` boundaries needs a
|
||||
same-shape permission test (see `tests/test_permission_boundaries.py`
|
||||
and `tests/test_button_actions.py` for the pattern: owner, linked user,
|
||||
unrelated user, logged out).
|
||||
- **UI changes**: verify in a real browser (Playwright), not just by
|
||||
reading the JS -- this project has hit multiple bugs that only showed up
|
||||
live (mobile viewport CSS collapse, a dialog's status message landing
|
||||
behind its own backdrop, a JSON/form-urlencoded body mismatch). Spin up
|
||||
`uvicorn app.main:app` against a scratch `DATABASE_URL`/`CONFIG_PATH`
|
||||
sqlite file, don't touch the real deployment's data.
|
||||
- **Deploy**: Gitea Actions at `git.thumeit.com/tfaour/espresso_frame`
|
||||
(`.gitea/workflows/server-docker-build.yml`: `test` -> `build-and-push`
|
||||
-> `deploy` on any push to `main` touching `server/**`; `deploy` SSHes
|
||||
into the host as `espressoframe_deployer` and runs `docker compose pull
|
||||
&& docker compose up -d`). A separate workflow
|
||||
(`firmware-release-build.yml`) builds+publishes firmware binaries as
|
||||
Gitea release assets when `firmware/version.txt` changes. Poll CI status
|
||||
with `curl https://git.thumeit.com/api/v1/repos/tfaour/espresso_frame/actions/tasks`
|
||||
rather than asking the user to check.
|
||||
- **Device-facing paths are frozen.** `/frame/image`, `/frame/advance`,
|
||||
`/frame/back`, `/frame/config`, `/frame/battery`, `/frame/firmware` and
|
||||
their exact JSON key names (`refresh_interval_s`, `firmware_version`,
|
||||
etc.) are baked into deployed firmware -- never rename or restructure
|
||||
these without a firmware-side migration story to match.
|
||||
Reference in New Issue
Block a user