35a9393537
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
5.1 KiB
5.1 KiB
travel-memories — Session Handover (2026-06-21)
What this is
services/travel-memories/ is a local Flask app that turns Immich photo albums into Grav CMS journal entries and story pages. It runs at http://localhost:8082 via Docker.
Current state
All 11 SDD tasks are complete. The app is fully functional end-to-end across all 6 phases. This session was spent fixing bugs discovered during real use and adding triage UX improvements.
Last commit: a265b08 — fix: use amber/sky colors for journal/story borders and badges
What was built this session
New triage UX (Phase 2)
Desktop:
- Selection ring: white ring on currently focused card; first card auto-selected on load
- Arrow key navigation:
←/→move the selection ring through photos - Enter: open lightbox for current card; click: also opens lightbox
- Lightbox: full-screen overlay —
←/→navigate,J/S/Xtag without closing,Escclose; badge + date shown at bottom - Badge: amber J / sky S / ghost X on every tagged photo; updates dynamically when tag changes
- Colored borders: amber (
border-amber-500) for journal, sky blue (border-sky-400) for story, dimmed for skip - Skip all untagged button: bulk-skips everything still untagged
Mobile (<768px):
- Tinder-style HammerJS swipe cards: right=journal, left=skip, up=story
- Card tilts + color overlay during drag (amber/sky/grey)
- Three tap buttons (X / J / S) below the card as alternative
- Back button with undo stack (max 10 actions)
- Progress bar synced with header counter
- Horizontal thumbnail strip at bottom: all photos, colored dot per tag, tap to jump to any photo
Bugs fixed this session
| Bug | Root cause | Fix |
|---|---|---|
| Triage badge not updating on tag change | Badge is server-rendered; JS wasn't updating it | Added updateBadge(el, tag) helper called after each tag |
| Arrow keys not working | Alpine modifier is left/right not arrowleft/arrowright |
Fixed modifier names |
| Dimmed photo stays dimmed after retag | Jinja classes have newlines → .split(' ') produces 'opacity-40\n' not 'opacity-40' |
Changed to .split(/\s+/).filter(c => c && ...) |
| Colored border disappears after retag | Filter !c.startsWith('border-') strips border-4 (width) too |
Re-add border-4 alongside color class |
| Borders appear white | DaisyUI border-success/border-info near-invisible in forest theme |
Use explicit Tailwind: border-amber-500, border-sky-400 |
| Badge text unreadable | DaisyUI badge semantic classes give poor contrast in forest theme |
Use bg-amber-500 text-black border-0 font-bold etc. |
Gotchas for next session
- Docker rebuild required after any template/code change:
docker compose build travel-memories && docker compose up -d --force-recreate travel-memories --force-recreaterequired to pick up.envchanges (plainrestartdoesn't re-read it)- Immich API key needs scopes:
album.read,asset.read,asset.download(Immich calls itasset.viewin some versions — check the Immich UI) - State directory permissions: if state/ was created as root, run
docker compose exec -u root travel-memories chown 1000:1000 /app/state - Never read
.env— contains real Immich credentials; pass to docker commands only
What's not done yet
Nothing was explicitly left incomplete — the pipeline works end-to-end. Potential next steps:
- Full-resolution lightbox: currently shows Immich preview thumbnail; could load
/proxy/original/<id>for the full-res image (endpoint may need adding toroutes/proxy.py) - End-to-end test for triage UX: the new JS-heavy triage UI has no Playwright coverage
- Phase 2 → real trip: use the app on the actual japan-korea-2026 Immich album
- Mobile swipe color consistency: swipe-right currently shows green overlay (intuitive for "go") — could switch to amber to match journal color, but debatable
File map
services/travel-memories/
├── app/
│ ├── __init__.py Flask factory
│ ├── immich.py Immich API client (x-api-key auth)
│ ├── state.py TripState / Photo models, atomic JSON R/W
│ └── routes/
│ ├── albums.py Phase 1 — album selection + slug sanitisation
│ ├── triage.py Phase 2 — tag/skip-untagged/done endpoints
│ ├── curate.py Phase 3 — reorder/swap
│ ├── group.py Phase 4 — grouping + dividers
│ ├── write.py Phase 5 — titles/captions
│ ├── export.py Phase 6 — write Grav markdown files
│ ├── proxy.py Immich thumbnail proxy
│ └── nav.py Shared nav context + stale propagation
│ └── templates/
│ ├── base.html DaisyUI forest + Alpine + HammerJS CDN
│ ├── phase1.html Album selection
│ ├── phase2.html Triage (desktop grid + mobile swipe + lightbox)
│ └── phase[3-6].html Curate, group, write, export
├── Dockerfile
└── docker-compose.yml Port 8082, UID/GID env vars, state volume