Files
intotheeast-com/docs/working/2026-06-21-travel-memories-handover.md
T
2026-06-21 21:41:24 +02:00

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/X tag without closing, Esc close; 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-recreate required to pick up .env changes (plain restart doesn't re-read it)
  • Immich API key needs scopes: album.read, asset.read, asset.download (Immich calls it asset.view in 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:

  1. Full-resolution lightbox: currently shows Immich preview thumbnail; could load /proxy/original/<id> for the full-res image (endpoint may need adding to routes/proxy.py)
  2. End-to-end test for triage UX: the new JS-heavy triage UI has no Playwright coverage
  3. Phase 2 → real trip: use the app on the actual japan-korea-2026 Immich album
  4. 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