Commit Graph

45 Commits

Author SHA1 Message Date
m038 e07fb3a72a feat(map): exit fullscreen on marker click, then scroll to entry
When fullscreen is active, clicking a marker now triggers fsBtn.click()
to exit cleanly (handles class, body overflow, tripMap.resize + icon),
then waits 450ms for the exit animation before scrolling to the entry
and firing the highlight. Also fixes missing icon-swap CSS for
.home-map-col.is-fullscreen (was only targeting .feed-map-wrap).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Vgmzx8VTTTmCskSpQtsLTr
2026-06-22 01:12:52 +02:00
m038 aa1cb7411c fix(map): collapse attribution on load; darken fullscreen button
Attribution: MapLibre v4 uses <details> and may open it after load
regardless of compact:true — remove the open attribute in the load
handler to guarantee collapsed state.

Button: switch from teal to --color-canvas (#22201B) so it sits quietly
against the dark map; icon reads in --color-ink (warm cream).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Vgmzx8VTTTmCskSpQtsLTr
2026-06-22 01:05:12 +02:00
m038 6f9538053c feat(map): mobile fullscreen button on trip page map
Button in bottom-right of #trip-map (z-index:1000), hidden ≥769px.
Attribution moved to bottom-left to free the corner. Clicking toggles
.is-fullscreen on .home-map-col (position:fixed, 100dvh), locks body
scroll, and calls tripMap.resize() for MapLibre to re-render.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Vgmzx8VTTTmCskSpQtsLTr
2026-06-22 00:56:57 +02:00
m038 02fc666661 feat(trip): pill radius on panel toggles, slide animation, mobile close button
- Radius: trip-panel-toggle now uses --radius-full, consistent with filter pills
- Animation: stats/cycling blocks use CSS grid-template-rows 0fr→1fr transition
  (inner trip-panel-inner div carries decoration so border/padding don't peek
  out when collapsed; display:none removed)
- Close button: ↑ Close stats / ↑ Close cycling at bottom of each panel,
  hidden ≥769px, triggers the header toggle via data-toggle attribute

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Vgmzx8VTTTmCskSpQtsLTr
2026-06-21 23:09:52 +02:00
m038 2b8ea1963b refactor(trip): declutter filter bar — move Stats/Cycling to panel toggles
Filter bar now has one job: content type (All/Journal/Stories) + sort icon (↑/↓).
Stats and Cycling move to a lean text-button row below the bar with a
rotating ▾ caret — CSS handles expand/collapse state via .is-active,
no JS changes needed for the caret animation.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Vgmzx8VTTTmCskSpQtsLTr
2026-06-21 22:58:28 +02:00
m038 b6142cee44 feat(trip): add ascending/descending sort toggle button
Button sits in the right filter group alongside Stats/Cycling.
Default state: ascending (↑ Oldest first, no highlight).
Toggled state: descending (↓ Newest first, is-active pill style).
DOM reversal uses insertBefore against the anchored #feed-filter-empty
so the empty-state message stays last regardless of sort direction.
Interacts safely with the type filter (show/hide by data-type).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Vgmzx8VTTTmCskSpQtsLTr
2026-06-21 22:21:55 +02:00
m038 53bfe5955d fix(photoswipe): target currSlide.container not currSlide.el
pswp.currSlide is a Slide instance whose DOM element is stored as
.container (.pswp__zoom-wrap). The .el property belongs to the
itemHolder wrapper, not the Slide — so currSlide.el was always
undefined, the null-guard exited early, and no animation played.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Vgmzx8VTTTmCskSpQtsLTr
2026-06-21 21:45:22 +02:00
m038 9f503c011d fix(photoswipe): keyboard arrow animation via CSS keyframes
Previous approach (CSS transition + reflow trick) is unreliable in
Firefox. New approach: PhotoSwipe emits 'change' synchronously before
painting; we add a direction-aware CSS keyframe animation to the
incoming slide element, with animation-fill-mode:both so there is no
flash before the animation starts.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Vgmzx8VTTTmCskSpQtsLTr
2026-06-21 21:36:48 +02:00
m038 415d95ed47 feat(photoswipe): animate keyboard arrow navigation in lightbox
PhotoSwipe's goTo() moves slides instantly (no spring animation unlike
swipe). Intercepts keydown in capture phase, sets a CSS transition and
forces a reflow before PhotoSwipe moves the container, so the browser
animates from the old position to the new one. Cleans up on close.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Vgmzx8VTTTmCskSpQtsLTr
2026-06-21 21:29:06 +02:00
m038 933652fd57 refactor(templates): extract entry markup into shared partials
Creates partials/entry-journal.html.twig and partials/entry-story.html.twig
so trip, dailies, and home all use the same up-to-date markup. Home page
gains PhotoSwipe, blurred fill, adaptive aspect ratio, and hash-based
marker scroll. Future changes only need to happen in one place.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Vgmzx8VTTTmCskSpQtsLTr
2026-06-21 21:07:12 +02:00
m038 bc77baca2e fix(scroll): clear URL hash when back-to-top is clicked
Uses history.pushState to strip the stale #entry-slug without
triggering a page jump, then smooth-scrolls to the top.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Vgmzx8VTTTmCskSpQtsLTr
2026-06-21 20:41:41 +02:00
m038 7c9a55224a fix(scroll): use hash navigation for marker clicks
Browser handles scroll natively via window.location.hash, respecting
scroll-margin-top. Updates URL for shareability and screen reader
compatibility. Added html { scroll-behavior: smooth } for smooth
hash navigation globally.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Vgmzx8VTTTmCskSpQtsLTr
2026-06-21 20:40:15 +02:00
m038 85ba3747b1 fix(scroll): use block:start so scroll-margin-top is respected
block:center ignores scroll-margin-top. block:start positions the
entry's top edge at the margin offset, clearing the sticky header.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Vgmzx8VTTTmCskSpQtsLTr
2026-06-21 20:36:46 +02:00
m038 b1492918d5 feat(trip): add back-to-top button
Reuses .story-totop styles. Appears after scrolling past 80% of
viewport height, smooth-scrolls to top on click.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Vgmzx8VTTTmCskSpQtsLTr
2026-06-21 20:29:07 +02:00
m038 71eaa3e788 feat(photos): adaptive aspect ratio per entry (portrait 4:5, landscape 4:3)
Portrait entries (first image taller than wide) get a 4:5 container —
Instagram's proven cap that prevents single photos dominating the screen.
Landscape entries keep 4:3. Aspect-ratio moved from slide to wrap so the
strip inherits it via height:100%.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Vgmzx8VTTTmCskSpQtsLTr
2026-06-21 20:18:05 +02:00
m038 5c75f1416f feat(photos): blurred ambient fill, dots overlay, PhotoSwipe on trip + dailies
- object-fit: contain + ::before blurred background fill on all slides
- dots moved inside photo-wrap, overlaid at bottom with shadow for contrast
- arrows hidden on touch devices via @media (hover: none)
- margin-bottom increased for breathing room below photo block
- trip.html.twig brought up to parity with dailies: same structure,
  same PhotoSwipe init, same expand button, same dot overlay

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Vgmzx8VTTTmCskSpQtsLTr
2026-06-21 20:09:24 +02:00
m038 eafc431e0e feat: expand connect markers to 4-mode select
Replaces the boolean toggle with a select field offering:
  on             — connect all consecutive entries (chronological line)
  manual         — force_connect entries only (user-controlled connections)
  intelligent_gpx — suppress connectors where GPX covers both endpoints;
                    force_connect overrides (original smart logic, restored)
  off            — no connectors at all; force_connect also ignored

buildJourneySegments gains an optional trackpointsPerFile param used
only by intelligent_gpx mode. renderGpxJourney extracts trackpoints
only when connectMode is intelligent_gpx. dailies.html.twig falls
back from intelligent_gpx → on (mini-map has no GPX tracks to
suppress against).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Vgmzx8VTTTmCskSpQtsLTr
2026-06-21 11:42:03 +02:00
m038 21b572677e feat: add per-trip use_gpx and autoconnect toggles
Adds two configurable toggles to the trip blueprint (Admin2 Trip tab):
- use_gpx: show/hide GPX tracks on all maps (default: enabled)
- autoconnect: draw connector lines between markers (default: enabled)

When use_gpx is off, GPX files are not fetched or rendered on any map
(home, map, trip, dailies). The stats panel in trip.html.twig still
reads GPX_URLS directly and is unaffected.

When autoconnect is off, buildJourneySegments suppresses all
auto-connectors; only entries with force_connect:true still draw a
line — making force_connect behaviour independent of both settings.

Also refactors the inline Promise.all in trip.html.twig to use the
shared renderGpxJourney utility (reducing duplication).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Vgmzx8VTTTmCskSpQtsLTr
2026-06-21 11:20:25 +02:00
m038 7c2303c4e8 fix: sort trip page entries ascending (oldest first) 2026-06-21 11:03:49 +02:00
m038 ce5d520817 feat(a11y): add aria-pressed to filter buttons and aria-expanded to stats/cycling toggles 2026-06-20 20:27:45 +02:00
m038 ffcf156289 feat: add story markers to trip map (white diamond); extend flash highlight to story cards 2026-06-20 17:53:56 +00:00
m038 20212fee25 perf: skip hero media lookup for journal entries — only story cards use it 2026-06-20 18:27:45 +02:00
m038 da7fbaf5b1 feat: replace journal entry card with inline journal-post in trip feed 2026-06-20 13:04:09 +00:00
m038 a398bcb737 feat: add map-to-card flash highlight on marker click 2026-06-20 12:47:51 +02:00
m038 2a151b710c refactor: collapse entry card article+a to flat <a>, unify hover targets across card types
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WPJztrVGbwic2xTG7G9fjM
2026-06-20 12:38:28 +02:00
m038 37c38e925a fix: add transport_mode to entry JSON serialisation in all three map templates; note bbox approach in isNearTrack 2026-06-20 00:54:04 +02:00
m038 b1665dad80 feat: use buildJourneySegments in trip.html.twig mini-map 2026-06-20 00:45:01 +02:00
m038 14e386a122 fix: remove 1m per-step elevation threshold — Komoot data is pre-smoothed, threshold filtered nearly all gain/loss 2026-06-19 23:34:39 +02:00
m038 8152fe79b6 fix: compute GPX stats per-file to avoid spurious inter-track segments
Both stats.html.twig and trip.html.twig previously flattened all GPX
trackpoints into a single masterPts array before computing haversine
distance, elevation, and moving time. This caused the junction between
file N's last point and file N+1's first point to be treated as a real
segment — e.g. Florence→coast (~79 km, ~42 h) for Italy's 3-file demo
data, overstating distance and moving time significantly.

Fix: compute all metrics within each file independently and sum the
results. fileResults collection and callback consumption are unchanged.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WPJztrVGbwic2xTG7G9fjM
2026-06-19 23:13:08 +02:00
m038 103ceb62b9 fix: deterministic GPX ordering in parseGpxFiles (trip.html.twig) 2026-06-19 23:06:28 +02:00
m038 c123a035ce feat: expand trip inline stats to 6 stats + add cycling panel with GPX parsing
- Expanded stats block from 4 to 6 stats (days, entries, countries, cities, distance, temp range)
- Added date_end-aware days calculation (uses header.date_end when available)
- Added cities dedup logic (seen_city_lower) matching Task 1 pattern
- Added temperature range computation (temp_min / temp_max)
- Added has_gpx boolean flag
- Distance label is conditional: km cycled (GPX) vs km roamed (no GPX)
- Stats note text is conditional to match distance mode
- Cycling button added to filter bar (only rendered when has_gpx)
- Cycling panel (7 stat blocks) added after stats block (hidden by default, toggled independently)
- Replaced old haversine IIFE with unified haversineKm + parseGpxFiles + IIFE
- GPX Mode A: fetches GPX files, sums trackpoint distances, populates cycling panel
- GPX Mode B: haversine between entry GPS points (no GPX)
- Updated .trip-stats-grid from repeat(4) to repeat(3) columns
- Added .trip-cycling-block, .trip-cycling-header, .trip-cycling-icon, .trip-cycling-title, .trip-cycling-grid CSS

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WPJztrVGbwic2xTG7G9fjM
2026-06-19 23:01:42 +02:00
m038 3ef8d48ee2 feat: add entry url to map_entries data and as data-url attribute on all markers 2026-06-19 22:18:04 +02:00
m038 997baf4cc3 fix: marker click scrolls to card on home/trip pages instead of navigating (no url field) 2026-06-19 22:14:58 +02:00
m038 044e74f5d3 feat: hover-only title tooltip on map markers; click navigates to entry 2026-06-19 22:05:52 +02:00
m038 f7df6ef37e fix: remove cooperativeGestures, increase fitBounds padding, add popups to embedded maps 2026-06-19 22:01:54 +02:00
m038 a363052f5f feat: migrate trip overview map to MapLibre GL (removes last Leaflet reference) 2026-06-19 21:57:12 +02:00
m038 0d1688c6c4 Revert "revert: remove out-of-scope stats block (belongs in separate task)"
This reverts commit a9043f711e.
2026-06-19 21:40:09 +02:00
m038 a9043f711e revert: remove out-of-scope stats block (belongs in separate task) 2026-06-19 21:39:42 +02:00
m038 897da36a21 feat: add inline stats block with toggle to trip page
Adds Twig computation for days on road, countries visited, and GPS
points; an expandable stats panel (hidden by default) with haversine
distance calculation; and toggle JS that activates the Stats button.
2026-06-19 21:35:54 +02:00
m038 eb739d80ab feat: wire up feed filter — All content / Journal / Stories
Added JavaScript to the trip.html.twig template that:
- Adds event listeners to filter buttons (.trip-filter-btn)
- Shows/hides article cards based on data-type attribute (journal/story)
- Manages active state of filter buttons
- Displays empty state message when no results match the filter
- Uses ES5 syntax (no arrow functions, const/let, or template literals)

Also added hidden feed-filter-empty element to display appropriate
empty messages for each filter type.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RB86BaJBG3eGiMdfhmHRrQ
2026-06-19 21:32:13 +02:00
m038 0478a18fa8 feat: add filter bar markup and pill button styles to trip page
Replace the old trip-nav links with a new filter bar component featuring:
- Three pill buttons for filtering (All content, Journal, Stories)
- "All content" button active by default with teal accent styling
- Separate Stats button with matching pill styling
- CSS for buttons with hover and active states
- Responsive flexbox layout that wraps on narrow screens

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RB86BaJBG3eGiMdfhmHRrQ
2026-06-19 21:29:33 +02:00
m038 2508936928 feat: add data-type attributes to feed cards; restyle story card with full border 2026-06-19 21:26:50 +02:00
m038 69c9f4f939 feat: trip page matches home layout — sticky map + feed, GPX route, no sidebar
- Same home-layout (45% sticky map / 55% scrollable feed) on every trip page
- GPX route overlay loaded from trip page media
- Marker click scrolls to entry card (same as home page)
- Map sub-nav link removed (map is now embedded)
- Separate /map page remains accessible by URL but has no nav link

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RB86BaJBG3eGiMdfhmHRrQ
2026-06-19 20:42:41 +02:00
m038 b66f1cdb2d feat: trip page — entry counts, merged feed, sticky sidebar index 2026-06-19 15:45:06 +02:00
m038 50a5f2d178 feat: add trip/trips/stories templates, update nav and map/stats to use trip-relative paths
- Rename tracker.html.twig to dailies.html.twig; update dailies.md template key
- Fix map.html.twig and stats.html.twig: find dailies via page.parent().route
- Update base.html.twig nav to use config.site.active_trip for all hrefs
- Fix dailies.html.twig mini-map link to use page.parent().url/map
- Create trip.html.twig, trips.html.twig, stories.html.twig

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 01:27:39 +02:00