diff options
| author | Paul Buetow <paul@buetow.org> | 2026-06-15 13:55:32 +0300 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2026-06-15 23:50:40 +0300 |
| commit | be6ae2c9541a01927616b46cf6a683d7ce4519ff (patch) | |
| tree | 53161337bae46495b8e88b96f1b28b1bd6f9c2ca /src | |
| parent | 6e47246dbbc97821b97aa9c45352abc9c2a9aa4a (diff) | |
Per-camera mini-albums: full-width grid + camera-scoped view navigation
Two fixes to the per-camera pages:
- The camera thumbnail grid is now full-width and reflows with the window like
the main album. Previously it sat inside the 900px-wide stats column, so wide
thumbnails only fit ~2 per row. camera.tmpl now puts the grid in a full-width
sibling block (centered by the body, like the album's direct thumbnail
children); the heading/back-link stay in the narrow stats column.
- Each camera is now a self-contained mini album. Thumbnails link to per-camera
view pages (camera-<slug>--<index>.html) whose prev/next cycle only through
that camera's photos, instead of linking into the shared album view pages
(where next/prev walked the whole album and left the camera). The view pages
reuse the album's look (blurred background, EXIF tooltip, animation) and offer
Gallery / Details (to the album details page) / Direct-link nav. The "--"
index separator can never collide with a gallery name since slugs never
contain "--".
Adds cameraview.tmpl + its render field, _stats_render_camera_{gallery,views}
and the cameraview body builders. Tests updated for the new thumbnail links and
extended to assert the per-camera view pages and their camera-scoped navigation;
required-context-vars test now also covers the camera and cameraview templates.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Diffstat (limited to 'src')
| -rw-r--r-- | src/lib/stats.source.sh | 204 | ||||
| -rw-r--r-- | src/lib/template.source.sh | 1 |
2 files changed, 159 insertions, 46 deletions
diff --git a/src/lib/stats.source.sh b/src/lib/stats.source.sh index 527ea1f..2488ea7 100644 --- a/src/lib/stats.source.sh +++ b/src/lib/stats.source.sh @@ -879,15 +879,14 @@ render_stats_page() { # field; camera.tmpl supplies only the page chrome (heading + back-to-stats # link), wrapped by the shared header/footer the same way render_stats_page does. # -# The album's incoming photo and thumbnail directories are the conventional -# 'photos'/'thumbs' that generate() passes to render_album_pages -# (render_album_pages 'photos' '.' 'thumbs' ...), so the grid reuses preview.tmpl's -# thumbnail markup style with those fixed dir names. Each thumbnail links to the -# full-size image under photos/ (backhref/photos/<file>) rather than a per-photo -# <page>-<preview>.html view page: a camera page is rendered outside the album -# pagination loop and cannot know which view page a given photo landed on, so the -# original image under photos/ is the only target derivable in this context (this -# choice is also documented in camera.tmpl). +# Each camera is a self-contained mini album: the gallery (camera-<slug>.html) +# shows a thumbnail grid, and every thumbnail links to a per-camera view page +# (camera-<slug>--<index>.html) whose prev/next navigation cycles only through +# that camera's photos. The "--<index>" suffix never collides with a gallery +# name because slugs never contain "--". The album's photo/thumb dirs are the +# conventional 'photos'/'thumbs' generate() passes to render_album_pages, and the +# view pages link "Details" to the album's own details page for the photo via the +# ALBUM_VIEW_PAGE_BY_PHOTO map. # Dist-relative subdirectories that hold the full-size images and thumbnails. # These mirror the literals generate() passes to render_album_pages so camera @@ -899,33 +898,23 @@ declare -gr STATS_CAMERA_THUMBS_DIR='thumbs' declare -gr STATS_BLURS_DIR='blurs' # Emit one thumbnail anchor for the camera grid: a thumb image (from thumbs/) -# wrapped in a link to the full-size image (from photos/), both resolved through -# the page's backhref. The photo filename is HTML-escaped because it ends up in -# both href and src attributes. Mirrors preview.tmpl's markup but targets the -# full image instead of a view page (see render_camera_pages header comment). +# wrapped in a link to this camera's view page for that photo +# (camera-<slug>--<index>.html), both resolved through the page's backhref. The +# photo filename is HTML-escaped because it ends up in the src attribute; the +# slug and index are filename-safe by construction. _stats_camera_thumbnail() { local -r backhref_html="$1"; shift + local -r slug="$1"; shift + local -ri index="$1"; shift local -r photo="$1"; shift local photo_html local animation_class - local view_page photo_html=$(_html_escape "$photo") - # Same seeded animation the album thumbnail uses for this photo, so camera - # pages animate identically. + # Same seeded animation the album thumbnail uses for this photo. animation_class=$(random_animation_css_class slow "$photo") - # Link to the photo's album view page (navigation, details, EXIF tooltip) - # so a camera page behaves exactly like the main album. Fall back to the - # full-size image when the album mapping is unknown (e.g. unit tests that - # render camera pages without a rendered album). - view_page="${ALBUM_VIEW_PAGE_BY_PHOTO[$photo]:-}" - if [ -n "$view_page" ]; then - printf ' <a name="%s" href="%s/%s.html">' \ - "$photo_html" "$backhref_html" "$view_page" - else - printf ' <a href="%s/%s/%s">' \ - "$backhref_html" "$STATS_CAMERA_PHOTOS_DIR" "$photo_html" - fi + printf ' <a name="%s" href="%s/camera-%s--%d.html">' \ + "$photo_html" "$backhref_html" "$slug" "$index" printf '<img class="thumb %s" src="%s/%s/%s" /></a>\n' \ "$animation_class" "$backhref_html" "$STATS_CAMERA_THUMBS_DIR" \ "$photo_html" @@ -936,20 +925,20 @@ _stats_camera_thumbnail() { # grid is deterministic for a given input. Returns the grid HTML on stdout. _stats_build_camera_thumbs() { local -r backhref_html="$1"; shift + local -r slug="$1"; shift local -r photos="$1"; shift local photo + local -i index=0 while IFS= read -r photo; do if [ -n "$photo" ]; then - _stats_camera_thumbnail "$backhref_html" "$photo" + (( ++index )) + _stats_camera_thumbnail "$backhref_html" "$slug" "$index" "$photo" fi done <<< "$photos" } -# Render a single camera-<slug>.html: header + camera.tmpl (escaped heading and -# pre-built thumbnail grid) + footer. The label is HTML-escaped via camera.tmpl's -# context_html field spec; backhref_html is the already-escaped path used inside -# the grid anchors so thumbnail links resolve from the page's location. +# Render one camera's mini album: the gallery page plus a view page per photo. _stats_render_camera_page() { local -r html_dir="$1"; shift local -r backhref="$1"; shift @@ -957,28 +946,151 @@ _stats_render_camera_page() { local -r slug="$1"; shift local -r photos="$1"; shift local backhref_html + + backhref_html=$(_html_escape "$backhref") + _stats_render_camera_gallery \ + "$html_dir" "$backhref" "$backhref_html" "$label" "$slug" "$photos" + _stats_render_camera_views \ + "$html_dir" "$backhref" "$backhref_html" "$slug" "$photos" +} + +# Render camera-<slug>.html: header + camera.tmpl (escaped heading and pre-built +# thumbnail grid) + footer. The label is HTML-escaped via camera.tmpl's +# context_html field spec; backhref_html is the already-escaped path used inside +# the grid anchors so thumbnail links resolve from the page's location. +_stats_render_camera_gallery() { + local -r html_dir="$1"; shift + local -r backhref="$1"; shift + local -r backhref_html="$1"; shift + local -r label="$1"; shift + local -r slug="$1"; shift + local -r photos="$1"; shift local camera_thumbs local background_image local -r page="camera-$slug.html" - backhref_html=$(_html_escape "$backhref") - camera_thumbs=$(_stats_build_camera_thumbs "$backhref_html" "$photos") + camera_thumbs=$(_stats_build_camera_thumbs "$backhref_html" "$slug" "$photos") background_image=$(_stats_random_background "$page") template header "$page" \ - html_dir "$html_dir" \ - backhref "$backhref" \ - blurs_dir "$STATS_BLURS_DIR" \ - background_image "$background_image" \ + html_dir "$html_dir" backhref "$backhref" \ + blurs_dir "$STATS_BLURS_DIR" background_image "$background_image" \ show_header_bar 'yes' template camera "$page" \ - html_dir "$html_dir" \ - backhref "$backhref" \ - camera_name "$label" \ - camera_thumbs "$camera_thumbs" + html_dir "$html_dir" backhref "$backhref" \ + camera_name "$label" camera_thumbs "$camera_thumbs" template footer "$page" \ - html_dir "$html_dir" \ - backhref "$backhref" \ - tarball_name '' + html_dir "$html_dir" backhref "$backhref" tarball_name '' +} + +# Render one camera-<slug>--<index>.html view page per photo, with prev/next +# wrapping around the camera's own photo list so navigation stays in the camera. +_stats_render_camera_views() { + local -r html_dir="$1"; shift + local -r backhref="$1"; shift + local -r backhref_html="$1"; shift + local -r slug="$1"; shift + local -r photos="$1"; shift + local photo + local -a photo_list=() + local -i i + + while IFS= read -r photo; do + [ -n "$photo" ] && photo_list+=("$photo") + done <<< "$photos" + + local -ri n=${#photo_list[@]} + for (( i = 1; i <= n; i++ )); do + _stats_render_camera_view_page \ + "$html_dir" "$backhref" "$backhref_html" "$slug" \ + "${photo_list[i - 1]}" "$i" \ + "$(( i == 1 ? n : i - 1 ))" "$(( i == n ? 1 : i + 1 ))" + done +} + +# Render a single per-camera view page (header + cameraview body + footer). +_stats_render_camera_view_page() { + local -r html_dir="$1"; shift + local -r backhref="$1"; shift + local -r backhref_html="$1"; shift + local -r slug="$1"; shift + local -r photo="$1"; shift + local -ri index="$1"; shift + local -ri prev="$1"; shift + local -ri next="$1"; shift + local body + local background_image + local -r page="camera-$slug--$index.html" + + body=$(_stats_build_cameraview_body \ + "$backhref_html" "$slug" "$photo" "$prev" "$next") + background_image=$(_stats_random_background "$page") + template header "$page" \ + html_dir "$html_dir" backhref "$backhref" \ + blurs_dir "$STATS_BLURS_DIR" background_image "$background_image" \ + show_header_bar 'no' + template cameraview "$page" \ + html_dir "$html_dir" backhref "$backhref" cameraview_body "$body" + template footer "$page" \ + html_dir "$html_dir" backhref "$backhref" tarball_name '' +} + +# Build the body of a per-camera view page: the photo (linked to this camera's +# next photo) plus a navigator whose prev/next cycle within the camera, a link +# back to the gallery, an optional Details link to the album's details page for +# the photo, and a direct image link. +_stats_build_cameraview_body() { + local -r backhref_html="$1"; shift + local -r slug="$1"; shift + local -r photo="$1"; shift + local -ri prev="$1"; shift + local -ri next="$1"; shift + local photo_html + local animation_class + local tooltip + local tooltip_attr='' + local view_page + local details_link='' + + photo_html=$(_html_escape "$photo") + animation_class=$(random_animation_css_class fast "$photo") + tooltip=$(photo_exif_tooltip_text "$photo" "$INCOMING_DIR/$photo") + if [ -n "$tooltip" ]; then + tooltip_attr=" title=\"$(_html_escape "$tooltip")\"" + fi + view_page="${ALBUM_VIEW_PAGE_BY_PHOTO[$photo]:-}" + if [ -n "$view_page" ]; then + details_link=$(printf ' <a href="%s/%s-details.html">Details</a> |' \ + "$backhref_html" "$view_page") + fi + _stats_print_cameraview_body "$backhref_html" "$slug" "$photo_html" \ + "$animation_class" "$tooltip_attr" "$details_link" "$prev" "$next" +} + +# Emit the per-camera view page markup. Split out so _stats_build_cameraview_body +# stays focused on assembling the pieces. +_stats_print_cameraview_body() { + local -r backhref_html="$1"; shift + local -r slug="$1"; shift + local -r photo_html="$1"; shift + local -r animation_class="$1"; shift + local -r tooltip_attr="$1"; shift + local -r details_link="$1"; shift + local -ri prev="$1"; shift + local -ri next="$1"; shift + + cat <<END +<div class='view'> + <a href="$backhref_html/camera-$slug--$next.html"> + <img class='view $animation_class' border='0' src='$backhref_html/$STATS_CAMERA_PHOTOS_DIR/$photo_html'$tooltip_attr /> + </a> + <div class="navigator"> + <a href="$backhref_html/camera-$slug--$prev.html" class="arrow">⇐</a> + <a href="$backhref_html/camera-$slug.html">Gallery</a> |$details_link + <a href="$backhref_html/$STATS_CAMERA_PHOTOS_DIR/$photo_html">Direct link</a> + <a href="$backhref_html/camera-$slug--$next.html" class="arrow">⇒</a> + </div> +</div> +END } # Public render entry point (handoff for task rm0). Renders one diff --git a/src/lib/template.source.sh b/src/lib/template.source.sh index 026e3ce..1fc8894 100644 --- a/src/lib/template.source.sh +++ b/src/lib/template.source.sh @@ -148,6 +148,7 @@ declare -ra TEMPLATE_RENDER_FIELD_SPECS=( 'render_background_image_css|context_css|background_image|background_image|header splash' 'render_blurs_dir_css|context_css|blurs_dir|blurs_dir|header splash' 'render_camera_name_html|context_html|camera_name|camera_name|camera' + 'render_cameraview_body_html|context_raw|cameraview_body|cameraview_body|cameraview' 'render_camera_thumbs_html|context_raw|camera_thumbs|camera_thumbs|camera' 'render_current_date_text|current_date_html|||' 'render_enter_page_html|context_html|enter_page|enter_page|splash' |
