docs: sync CLAUDE.md + reference/guides with standalone-page cleanup
- Replace 'Shared feed-map partial' section with 'entry-map' partial + the single MapUtils.initEntryMap map path - Trip architecture: two content subfolders (dailies/stories as inert routable:false containers); map/stats inline on trip page - Fix stale references to deleted map/stats/dailies/stories templates in architecture.md (template list, page tree, GPX flow) and trip-switching.md (scaffold + verify steps) - Correct GPX rendering: toGeoJSON bundled in map.js, no leaflet CDN - Pre-launch smoke test now checks the trip page feed, not /dailies Left docs/working/* (milestones, learnings, QA, plans) as point-in-time history. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -27,39 +27,48 @@ The Docker dev server runs at **http://localhost:8081** (mapped from container p
|
|||||||
The site is structured around Trip entities. Key facts:
|
The site is structured around Trip entities. Key facts:
|
||||||
- Active trip is set in `user/config/site.yaml` → `active_trip: japan-korea-2026`
|
- Active trip is set in `user/config/site.yaml` → `active_trip: japan-korea-2026`
|
||||||
- Trip pages live at `user/pages/01.trips/<slug>/`
|
- Trip pages live at `user/pages/01.trips/<slug>/`
|
||||||
- Each trip has: `01.dailies/`, `02.map/`, `03.stats/`, `04.stories/`
|
- Each trip has two content subfolders: `01.dailies/` (journal entries) and `04.stories/` (stories). The former `02.map/` and `03.stats/` standalone views were **removed** (2026-07-04, see `docs/working/plans/2026-07-04-standalone-page-cleanup.md`) — map and stats now render inline on the trip page
|
||||||
|
- `01.dailies/` and `04.stories/` are `routable:false` **data containers** — visiting `/trips/<slug>/dailies` or `/stories` directly 404s/redirects; their children (entries/stories) render at their own detail URLs and are aggregated by the trip page
|
||||||
- Site nav in `base.html.twig` has Home + Past Trips only — does not link to trip sub-sections
|
- Site nav in `base.html.twig` has Home + Past Trips only — does not link to trip sub-sections
|
||||||
- Post form parent (`post-form.md` → `pageconfig.parent`) **must be kept in sync** with `active_trip`
|
- Post form parent (`post-form.md` → `pageconfig.parent`) **must be kept in sync** with `active_trip`
|
||||||
- The trip page (`trip.html.twig`) uses a **client-side filter bar** (All content / Journal / Stories) — do NOT add nav links back to `/dailies`, `/stats`, `/stories` on the trip page. This filter bar + stats chrome is shared with the home active-trip view via the `trip-feed-col` partial (see "Shared trip-feed-col partial" below)
|
- The trip page (`trip.html.twig`) uses a **client-side filter bar** (All content / Journal / Stories). The standalone `/dailies`, `/map`, `/stats`, `/stories` view pages no longer exist — do NOT try to re-create them or link to them. This filter bar + stats chrome is shared with the home active-trip view via the `trip-feed-col` partial (see "Shared trip-feed-col partial" below)
|
||||||
- Stats are shown inline on the trip page via a toggle; the standalone `/stats` sub-page still exists as a URL but is not linked from the trip page
|
- Stats are shown inline on the trip page via a toggle (the standalone `/stats` view was removed)
|
||||||
- GPX route files live as media on the trip page itself, served via leaflet-gpx CDN
|
- GPX route files live as media on the trip page itself, parsed client-side via toGeoJSON (bundled into `js/map.js`) and drawn on the trip/home map
|
||||||
- Manage GPX files (view/upload/delete) at `/gpx-manager` — requires admin login; filenames are auto-slugified on upload
|
- Manage GPX files (view/upload/delete) at `/gpx-manager` — requires admin login; filenames are auto-slugified on upload
|
||||||
|
|
||||||
### Shared feed-map partial
|
### One map path: `MapUtils.initEntryMap` + the `entry-map` partial
|
||||||
|
|
||||||
The mini-map above the feed is shared across two pages via a Twig partial:
|
There is a **single** map code path on the site. The engine is `MapUtils.initEntryMap(opts)` in `js/src/maplibre-utils.js` (bundled into `js/map.js` via `make build-assets` — never hand-edit `js/map.js`). It builds the MapLibre map, places markers/popups, fits bounds, draws the GPX journey, and wires the fullscreen toggle.
|
||||||
|
|
||||||
- **Partial:** `user/themes/intotheeast/templates/partials/feed-map.html.twig`
|
The map **markup + invocation** is shared via one partial:
|
||||||
- **Used by:** `dailies.html.twig` and `stories.html.twig`
|
|
||||||
- **NOT used by:** `trip.html.twig` (uses its own `#trip-map` / `.home-map-col` layout)
|
|
||||||
|
|
||||||
**Parameters (passed via `{% include ... with {...} only %}`):**
|
- **Partial:** `user/themes/intotheeast/templates/partials/entry-map.html.twig`
|
||||||
|
- **Used by:** `trip.html.twig` and the active branch of `home.html.twig` (both via `{% include ... with {...} only %}`)
|
||||||
|
|
||||||
| Parameter | Type | Description |
|
It renders the `.home-map-col` column (map div `#{{ map_id }}` + fullscreen button) and, when `entries` is non-empty, a thin `<script>` that assigns `window.{{ map_global }}` from `initEntryMap`. Callers resolve header values (use_gpx / autoconnect) and pass them in.
|
||||||
|---|---|---|
|
|
||||||
| `map_entries` | array | `[{lat, lng, title, slug, url, type, force_connect, transport_mode}]` |
|
|
||||||
| `map_id` | string | HTML id for map div: `'feed-map'` or `'stories-map'` |
|
|
||||||
| `map_var` | string | JS global variable: `'feedMap'` or `'storiesMap'` |
|
|
||||||
| `link_href` | string\|null | "View full map" link URL; `null` hides it |
|
|
||||||
| `card_prefix` | string | Scroll-to ID prefix: `'entry-'` (dailies) or `'story-'` (stories) |
|
|
||||||
| `trip_page` | Page | Trip page object for autoconnect setting |
|
|
||||||
| `show_journey` | bool | `true` draws the route connector; `false` skips it |
|
|
||||||
|
|
||||||
The partial always: starts attribution collapsed, shows the fullscreen button (mobile-only, CSS `display:none` ≥769px), and on marker click scrolls to `#<card_prefix><slug>` + flashes `.is-highlighted`.
|
**Parameters:**
|
||||||
|
|
||||||
|
| Parameter | Type | Trip passes | Home passes |
|
||||||
|
|---|---|---|---|
|
||||||
|
| `map_id` | string | `'trip-map'` | `'home-map'` |
|
||||||
|
| `map_global` | string | `'tripMap'` | `'homeMap'` |
|
||||||
|
| `entries` | array | `[{lat, lng, slug, title, url, type?, force_connect, ...}]` | same |
|
||||||
|
| `card_prefix` | string | `'entry-'` | `'entry-'` |
|
||||||
|
| `story_markers` | bool | `true` (diamond markers) | `false` |
|
||||||
|
| `gpx_urls` | array | `gpx_urls` | `home_gpx_urls` |
|
||||||
|
| `use_gpx` | bool | `page.header.use_gpx ?? true` | derived from `trip.header` |
|
||||||
|
| `autoconnect` | string | `page.header.autoconnect ?? 'on'` | derived from `trip.header` |
|
||||||
|
| `gpx_source_prefix` | string | `'gpx'` | `'home-gpx'` |
|
||||||
|
| `journey_id` | string | `'trip-journey'` | `'home-journey'` |
|
||||||
|
|
||||||
|
The map globals `window.tripMap` / `window.homeMap` are asserted by the Playwright map specs, so any surface using this partial must keep assigning them.
|
||||||
|
|
||||||
|
> History: this replaced the old three-variant setup (a `feed-map.html.twig` partial with its own inline init, plus a full-page `map.html.twig`). Those were deleted in the 2026-07-04 standalone-page cleanup; the `2026-06-27-map-init-consolidation` plan had already moved trip + home onto `initEntryMap`.
|
||||||
|
|
||||||
### Shared trip-feed-col partial
|
### Shared trip-feed-col partial
|
||||||
|
|
||||||
The home page's active-trip view and the trip page render the **same feed-col chrome** (date-range header, filter bar, stats/cycling panels, feed loop) via one shared Twig partial. This is separate from the feed-map partial above — it is the column **beside** the map, not the map.
|
The home page's active-trip view and the trip page render the **same feed-col chrome** (date-range header, filter bar, stats/cycling panels, feed loop) via one shared Twig partial. This is separate from the `entry-map` partial above — it is the column **beside** the map, not the map.
|
||||||
|
|
||||||
- **Partial:** `user/themes/intotheeast/templates/partials/trip-feed-col.html.twig`
|
- **Partial:** `user/themes/intotheeast/templates/partials/trip-feed-col.html.twig`
|
||||||
- **Used by:** `trip.html.twig` and the active branch of `home.html.twig` (both via `{% include ... with {...} only %}`)
|
- **Used by:** `trip.html.twig` and the active branch of `home.html.twig` (both via `{% include ... with {...} only %}`)
|
||||||
@@ -84,7 +93,7 @@ The home page's active-trip view and the trip page render the **same feed-col ch
|
|||||||
|
|
||||||
### GPX file management
|
### GPX file management
|
||||||
|
|
||||||
GPX files are stored as page media on the trip page (`user/pages/01.trips/<slug>/`). They are picked up automatically by `map.html.twig` via `trip_page.media.all`.
|
GPX files are stored as page media on the trip page (`user/pages/01.trips/<slug>/`). They are picked up automatically by `trip.html.twig` (and `home.html.twig`) via `trip_page.media.all`, filtered to `.gpx`, and passed to the shared `entry-map` partial.
|
||||||
|
|
||||||
The GPX manager page (`user/pages/03.gpx-manager/`) provides a browser UI at `/gpx-manager`:
|
The GPX manager page (`user/pages/03.gpx-manager/`) provides a browser UI at `/gpx-manager`:
|
||||||
- **Auth:** enforced by Login plugin via `access.admin.login: true` in frontmatter — shows login form if not authenticated
|
- **Auth:** enforced by Login plugin via `access.admin.login: true` in frontmatter — shows login form if not authenticated
|
||||||
@@ -109,7 +118,7 @@ Two places hardcode the active trip slug. Grav's config and page frontmatter are
|
|||||||
|
|
||||||
Note: `system.yaml` `home.alias` is permanently set to `/home` (the real home page) and does **not** need to change when switching trips.
|
Note: `system.yaml` `home.alias` is permanently set to `/home` (the real home page) and does **not** need to change when switching trips.
|
||||||
|
|
||||||
After updating, also create the new trip's page tree under `user/pages/01.trips/<new-slug>/` with the standard four subfolders.
|
After updating, also create the new trip's page tree under `user/pages/01.trips/<new-slug>/` with the two content subfolders `01.dailies/` and `04.stories/` (each with an inert `routable:false` container `.md`), plus the trip's `trip.md`. Do **not** recreate `02.map/` or `03.stats/` — those standalone views were retired.
|
||||||
|
|
||||||
### Environment
|
### Environment
|
||||||
|
|
||||||
@@ -155,7 +164,7 @@ Before going live, change in `user/config/system.yaml`:
|
|||||||
|---|---|---|
|
|---|---|---|
|
||||||
| `twig.cache` | `true` | Templates compiled once and reused; safe because theme files don't change at runtime |
|
| `twig.cache` | `true` | Templates compiled once and reused; safe because theme files don't change at runtime |
|
||||||
|
|
||||||
**Pre-launch smoke test required:** with `twig.cache: true`, submit one post via `/post` and confirm the entry appears in `/trips/italy-2026-demo/dailies` immediately. This verifies the cache-on-save plugin (BUG-001 fix) works correctly with caching enabled.
|
**Pre-launch smoke test required:** with `twig.cache: true`, submit one post via `/post` and confirm the entry appears in the trip page feed at `/trips/italy-2026-demo` immediately. This verifies the cache-on-save plugin (BUG-001 fix) works correctly with caching enabled.
|
||||||
|
|
||||||
### What the cache-on-save plugin handles
|
### What the cache-on-save plugin handles
|
||||||
|
|
||||||
|
|||||||
@@ -42,23 +42,21 @@ pageconfig:
|
|||||||
|
|
||||||
## Step 3 — Create the new trip page tree
|
## Step 3 — Create the new trip page tree
|
||||||
|
|
||||||
Create the standard four subfolders under `user/pages/01.trips/<new-slug>/`:
|
Create the two content subfolders under `user/pages/01.trips/<new-slug>/`:
|
||||||
|
|
||||||
```
|
```
|
||||||
user/pages/01.trips/japan-korea-2026/
|
user/pages/01.trips/japan-korea-2026/
|
||||||
├─ trip.md ← title, date_start, date_end, cover_image, album_url
|
├─ trip.md ← title, date_start, date_end, cover_image, album_url
|
||||||
├─ 01.dailies/
|
├─ 01.dailies/
|
||||||
│ └─ dailies.md ← template: dailies (list page)
|
│ └─ dailies.md ← inert container: template: default, routable: false, visible: false
|
||||||
├─ 02.map/
|
|
||||||
│ └─ map.md ← template: map
|
|
||||||
├─ 03.stats/
|
|
||||||
│ └─ stats.md ← template: stats
|
|
||||||
└─ 04.stories/
|
└─ 04.stories/
|
||||||
└─ stories.md ← template: stories
|
└─ stories.md ← inert container: template: default, routable: false, visible: false
|
||||||
```
|
```
|
||||||
|
|
||||||
Copy these files from an existing trip and update the frontmatter (especially `title` and `date_start` in `trip.md`).
|
Copy these files from an existing trip and update the frontmatter (especially `title` and `date_start` in `trip.md`).
|
||||||
|
|
||||||
|
> The `02.map/` and `03.stats/` standalone views were retired (2026-07-04) — the map and stats render inline on the trip page. The `01.dailies/` and `04.stories/` folders now exist only as data containers holding the entry/story children; their own routes are non-routable.
|
||||||
|
|
||||||
Fields in `trip.md` to update:
|
Fields in `trip.md` to update:
|
||||||
|
|
||||||
| Field | Example | Notes |
|
| Field | Example | Notes |
|
||||||
@@ -85,5 +83,5 @@ This commits and pushes the `user/` repo to Gitea. The webhook triggers a produc
|
|||||||
|
|
||||||
After pushing, check:
|
After pushing, check:
|
||||||
1. Home page shows the new trip (title and date)
|
1. Home page shows the new trip (title and date)
|
||||||
2. Submit a test entry via `/post` — verify it lands under `user/pages/01.trips/<new-slug>/01.dailies/`
|
2. Submit a test entry via `/post` — verify it lands under `user/pages/01.trips/<new-slug>/01.dailies/` and appears in the feed at `/trips/<new-slug>`
|
||||||
3. Map at `/trips/<new-slug>/map` shows the correct (empty or GPX-only) state
|
3. The inline map on `/trips/<new-slug>` shows the correct (empty or GPX-only) state
|
||||||
|
|||||||
@@ -13,8 +13,8 @@ How the intotheeast site hangs together.
|
|||||||
| Container | Docker (`getgrav/grav` base + custom `Dockerfile`) | Grav 2.0 baked in at build time |
|
| Container | Docker (`getgrav/grav` base + custom `Dockerfile`) | Grav 2.0 baked in at build time |
|
||||||
| PHP session | `session.save_path = /tmp` | Set in `php/php-local.ini` |
|
| PHP session | `session.save_path = /tmp` | Set in `php/php-local.ini` |
|
||||||
| Dev URL | http://localhost:8081 | Mapped from container port 80 |
|
| Dev URL | http://localhost:8081 | Mapped from container port 80 |
|
||||||
| Maps | MapLibre GL JS | Replaced Leaflet; all 3 map templates use it |
|
| Maps | MapLibre GL JS | Replaced Leaflet; one shared map path (`MapUtils.initEntryMap`) on trip + home |
|
||||||
| GPX rendering | maplibre-gl-leaflet-gpx (CDN) | Renders GPX files as route layers |
|
| GPX rendering | toGeoJSON (bundled in `js/map.js`) | Parses GPX → GeoJSON route layers client-side; no CDN |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -62,15 +62,13 @@ templates/
|
|||||||
├─ home.html.twig ← extends base; context-aware two-column layout
|
├─ home.html.twig ← extends base; context-aware two-column layout
|
||||||
├─ trip.html.twig ← extends base; trip page with filter bar (All/Journal/Stories)
|
├─ trip.html.twig ← extends base; trip page with filter bar (All/Journal/Stories)
|
||||||
├─ entry.html.twig ← extends base; single journal entry (gallery, badges, map)
|
├─ entry.html.twig ← extends base; single journal entry (gallery, badges, map)
|
||||||
├─ dailies.html.twig ← extends base; journal feed list
|
|
||||||
├─ map.html.twig ← extends base; full-height MapLibre trip map
|
|
||||||
├─ stats.html.twig ← extends base; trip stats (days, distance, elevation)
|
|
||||||
├─ stories.html.twig ← extends base; stories grid
|
|
||||||
├─ story.html.twig ← extends base; single story (Ken Burns hero, shortcodes)
|
├─ story.html.twig ← extends base; single story (Ken Burns hero, shortcodes)
|
||||||
└─ gpx-manager.html.twig ← extends base; admin UI for GPX file management
|
└─ gpx-manager.html.twig ← extends base; admin UI for GPX file management
|
||||||
```
|
```
|
||||||
|
|
||||||
Partials live in `templates/partials/`. Currently one partial: `base.html.twig` (the site shell extended by all page templates).
|
The standalone `dailies.html.twig`, `map.html.twig`, `stats.html.twig` and `stories.html.twig` view templates were **removed** in the 2026-07-04 standalone-page cleanup — the trip page (`trip.html.twig`) consolidated the feed, inline map, and inline stats.
|
||||||
|
|
||||||
|
Partials live in `templates/partials/` (plus macros in `templates/macros/`). Key partials: `base.html.twig` (site shell extended by all page templates), `entry-map.html.twig` (shared map column + `initEntryMap` call, used by trip + home), `trip-feed-col.html.twig` (feed column chrome, shared by trip + home), `home-predeparture.html.twig`, `entry-journal.html.twig` / `entry-story.html.twig` (feed cards), and `weather-icons.html.twig`.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -82,13 +80,13 @@ The site is organized around Trip entities. The active trip is set in `user/conf
|
|||||||
user/pages/01.trips/
|
user/pages/01.trips/
|
||||||
└─ japan-korea-2026/
|
└─ japan-korea-2026/
|
||||||
├─ trip.md ← template: trip; title, date_start, cover_image, album_url
|
├─ trip.md ← template: trip; title, date_start, cover_image, album_url
|
||||||
├─ *.gpx ← GPX route files (served as page media; auto-detected by map.html.twig)
|
├─ *.gpx ← GPX route files (served as page media; auto-detected by trip.html.twig)
|
||||||
├─ 01.dailies/ ← journal entries (template: dailies list + entry children)
|
├─ 01.dailies/ ← journal entry children (container .md is routable:false)
|
||||||
├─ 02.map/map.md ← template: map
|
└─ 04.stories/ ← story children (container .md is routable:false)
|
||||||
├─ 03.stats/stats.md ← template: stats
|
|
||||||
└─ 04.stories/ ← story pages (template: stories list + story children)
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
`01.dailies/` and `04.stories/` are inert data containers — the trip page aggregates their children; visiting the container routes directly 404s/redirects. (The former `02.map/` and `03.stats/` folders were removed with their view templates.)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## GPX data flow
|
## GPX data flow
|
||||||
@@ -100,10 +98,10 @@ GPX file uploaded to trip page media
|
|||||||
user/pages/01.trips/<slug>/*.gpx
|
user/pages/01.trips/<slug>/*.gpx
|
||||||
│
|
│
|
||||||
▼
|
▼
|
||||||
map.html.twig: trip_page.media.all → filter .gpx files → pass as JS array
|
trip.html.twig / home.html.twig: trip_page.media.all → filter .gpx → entry-map partial
|
||||||
│
|
│
|
||||||
▼
|
▼
|
||||||
MapLibre source: each GPX file added as a GeoJSON source via maplibre-gl-leaflet-gpx
|
MapLibre source: each GPX file parsed by toGeoJSON (bundled in js/map.js) → GeoJSON source
|
||||||
│
|
│
|
||||||
▼
|
▼
|
||||||
Connector suppression: same-file 10km proximity check prevents spurious inter-track segments
|
Connector suppression: same-file 10km proximity check prevents spurious inter-track segments
|
||||||
|
|||||||
Reference in New Issue
Block a user