The clock-icon popup in the workout editor was capped at max-h-80
(~320px = ~5 history rows). Users with multi-year history saw older
sessions hidden behind a tiny inner scrollbar. Bumped to 70vh so it
scales with the viewport — ~15+ rows on a normal display, more on a
large monitor.
The IntersectionObserver pagination already loaded more rows on
demand; the old cap just kept them off-screen.
Pure CSS-class change. No schema, no API, no data.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two surfaces had invisible 50-row caps that this commit removes.
Exercise history popup (clock button in WorkoutForm):
- /api/exercises/[id] now accepts ?offset=N&limit=N (default 25,
max 100) and returns { exercise, history, hasMore }. Pagination
uses take: limit + 1 to detect hasMore without a second COUNT
round-trip.
- Query rewritten to use Prisma's setLogs.some filter — single SQL
that hits the (userId, deletedAt, date) composite index, instead
of fetching all set logs then grouping in JS.
- ExerciseHistoryPopup now uses an IntersectionObserver on a
sentinel div. When sentinel scrolls into view (root: the popup
itself, not the viewport), fetches next page and appends. Status
row at the bottom shows a spinner while loading and "End of
history" when done.
- Container max height bumped from h-64 -> h-80 for a bit more
breathing room on first render.
Workout history page (/main/workouts):
- Page still server-renders the first 50 workouts (instant paint
+ correct date filter forwarding). Now uses take: PAGE_SIZE + 1
to detect hasMore.
- New WorkoutsList client component takes initial workouts +
hasMore + filter values as props. IntersectionObserver on a
sentinel below the cards auto-fetches the next page from
/api/workouts?offset=N&limit=50&q=...&dateFrom=...&dateTo=...
when scrolled to. Filters round-trip through URL params, so a
filter change re-renders the page from scratch with a fresh
first page.
- "End of history · N workouts" line shown once everything is
loaded.
Tests:
- tests/routes-exercise-history.test.ts: 6 new tests covering
auth, cross-user 404, first-page hasMore=true, second-page
hasMore=false + no overlap, set-log filter scoped to the
queried exerciseId, soft-deleted workouts excluded.
- All 87 tests pass.
No schema changes, no migration. /data untouched.
Repo cleanup
- Add top-level .gitignore (was missing; node_modules, .next, *.s9pk,
image.tar, seed/data/*.db, log files, etc.) and a root README.
- Delete legacy start9/0.3.5/ package (StartOS 0.3.5 wrapper, no longer
the deploy target).
- Delete start9-example-packaging/ (template from another project).
- Delete planning docs (START9_PACKAGING_LOG.md, VERSIONING.md,
STARTOS_0.4_UPGRADE_PROMPT.md, ICON_FILES_INDEX.md, etc.) — info now
lives in the deploy guide and code comments.
- Drop the standalone Dockerfile, docker-compose.yml, ICON_*, and dev
log/build artifacts from the app dir.
- Drop the v0.1.0:18/19/20 version files (they belonged to the legacy
workout-log package and don't apply to the new id).
Rename + new package
- Rename app dir workout-planner/ -> proof-of-work/.
- Rename StartOS package id workout-log -> proof-of-work; the new id
makes this a brand new StartOS service (clean cutover from the old
one rather than in-place upgrade).
- Reset version graph; v1.0.0:1 is the seeded cutover release. The
Dockerfile bakes a one-time /data snapshot and docker_entrypoint.sh
copies it into the new volume on truly-fresh first boot only (both
/data/app.db missing AND /data/.seeded absent).
- Move start9/0.4-migration/ -> start9/0.4/; the old start9/0.4/ stub
is gone.
Curated exercise library (multi-user-aware)
- proof-of-work/prisma/exercises.seed.json is the canonical library
shipped to every install (164 exercises today, dumped from the live
snapshot).
- proof-of-work/scripts/sync-library.cjs (npm run sync-library) refreshes
the JSON from start9/0.4/seed/data/app.db after refresh_seed.sh.
- proof-of-work/prisma/seed.ts now reads from the JSON instead of a
hardcoded 52-exercise array; runs at Docker build time to seed the
fallback DB and on first boot for fresh installs.
- proof-of-work/prisma/ensureExerciseLibrary.cjs runs on every container
boot (from docker_entrypoint.sh) and INSERT OR IGNOREs every library
entry for every user, keyed on (userId, name). Library updates flow
to existing installs on package upgrade; user-custom exercises
(isCustom=true) and any colliding names are never overwritten;
removed exercises stay on existing installs (additive-only).
Deploy guide (start9/0.4/DEPLOY_040.md)
- Rewritten end-to-end for the workout-log -> proof-of-work cutover:
refresh_seed, sync-library, build, sideload, verify, rotate creds,
stop the old service, then post-cutover cleanup release v1.0.0:2.