| Age | Commit message (Collapse) | Author |
|
The footer "Site generated ... with <link>" was hardcoded to the shuriken.sh
repository. Make it configurable via the SOURCE_URL config variable and the
--source-url CLI flag (defaulting to the shuriken.sh repo, so existing sites are
unchanged). The footer derives the displayed text from the URL by stripping its
scheme. Plumbed through apply_config_defaults, CLI override targets/spec,
--print-config, the verbose effective-config log, and the header template's
new render_source_url_html (config_html) render var. Documented in
shuriken.default.conf and README; added a generation test asserting a custom
SOURCE_URL replaces the default footer link, and updated the print-config and
header render-var-subset expectations.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
|
prepare_template_render_vars previously computed and serialized every one
of the 30+ render_* fields for each template invocation, even though e.g.
the header template references only a handful. It now computes only the
subset the target template actually needs.
The needed set is driven by the required_templates (5th) field of each
TEMPLATE_RENDER_FIELD_SPECS entry, corrected/completed so every spec's
required_templates exactly matches that render_var's references in the
.tmpl files (config_html and derived kinds previously left it empty). A
new template_needed_render_vars_to builds the per-template set;
prepare_template_render_vars takes the template name and skips non-needed
fields; serialize_template_render_context emits only the computed keys
into the BASH_ENV context file.
Dependency closure: handlers read only from the input context array or
config globals, never from another computed render_var, so the direct
per-template set is the full closure (no transitive expansion needed).
Side-effects: every handler is a pure value computation; none consume
RANDOM/seed; current_date_html only primes the deterministic
SHURIKEN_CURRENT_DATE_TEXT cache (idempotent), so subsetting is safe for
all fields. render_html_dir_html (required_templates='*') is referenced by
no template but kept always-computed as a documented cheap exception.
Output is byte-identical: diff -r over a full generated dist/ (3-image,
2-camera fixture, STATS_PAGE=yes, fixed random seed) between the previous
bin/shuriken and this build matches exactly across all 63 files (51 HTML
album/details/splash/stats/per-camera/filter pages + tarball).
Tests: add test_template_render_vars_subset_is_minimal_for_header (header
computes only its needed vars, succeeds with unrelated config globals
unset) and test_template_render_var_subsetting_matches_templates (spec
needed-set equals each .tmpl's render_* references). Existing
test_template_required_context_vars_come_from_render_specs is unchanged.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
|
The preview_num next/prev render handlers computed neighbour page numbers
with $(( context_value +/- 1 )) but only guarded against an empty value.
A non-numeric preview_num context value (e.g. a stray string) slipped past
the [ -n ] check and triggered a bash arithmetic syntax error which, under
set -e, aborted the whole script.
Validate the context value is a non-negative integer
([[ value =~ ^[0-9]+$ ]]) before the arithmetic in both
prepare_template_render_var__preview_num_next_html and __preview_num_prev_html.
Invalid or missing values now default to an empty render value, matching the
existing missing-neighbour behaviour; the valid-numeric path is unchanged.
Add test_template_render_var_preview_num_guards_non_numeric in tests/cli.sh,
which drives the handlers directly under bash -euo pipefail to prove a bad
preview_num no longer crashes and a numeric one still yields the exact
+1 / -1 neighbour.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
|
Each thumbnail on a preview page (page-N.html) used to be rendered by its
own "template preview" call, paying the full source_template_file cost --
an "env -i bash" invocation -- per thumbnail. With MAXPREVIEWS thumbnails
per page that was N template renders per page just for the grid.
render_full_preview_page now builds the markup for ALL of a page's
thumbnails in bash (build_preview_thumbnail / append_preview_thumbnail)
and emits the whole grid in ONE render via a new previewpage.tmpl that
takes the pre-built HTML through a context_raw "preview_thumbs" field --
the same pattern the stats filter galleries (camera.tmpl) already use.
Per-thumbnail markup is byte-identical to the old preview.tmpl output:
same <a name=... href=...><img class='thumb <anim>' .../></a> structure,
order, HTML escaping and seeded "slow" animation class. Header and footer
stay as their own template calls, so a page now costs ~1 previewpage
render + header/footer instead of N + chrome.
The parallel job-pool integration and failure contract are unchanged:
each preview page is still one background render job. Added the
render_preview_thumbs_html field spec (hn0 dispatch pattern, context_raw
kind), registered previewpage in the validate_template_dir required
templates and in the required-context-vars test expectations, and pointed
the four generation template-failure tests at previewpage.tmpl (generation
no longer renders preview.tmpl). The standalone "template preview" engine
unit tests keep exercising preview.tmpl, which still ships.
Verified byte-identical output: generated the fixture album (including a
spaces/special-char filename) twice with RANDOM_SEED=42 using the parent
commit's bin/shuriken vs the new bin; every .html file is identical.
just test, just shellcheck, just check-generated and git diff --check all
pass.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
|
Replace the hardcoded `case "$kind"` block in
prepare_template_render_vars with a name-based registration/dispatch
pattern. Each render field kind is now implemented by one
prepare_template_render_var__<kind> handler; the core loop resolves the
handler by name (prepare_template_render_var__$kind), verifies it exists
via `declare -F`, calls it with a uniform signature
(out_nameref, context_name, source_name), and reports a config_error for
an unknown kind (no matching handler) -- preserving the previous error
behavior. Adding a new kind now means defining a new handler function
only; the loop never changes.
Handlers cover all existing kinds: context_css, context_html,
context_raw, current_date_html, config_html (keeps its inner source_name
dispatch for HEIGHT/MAXPREVIEWS/TITLE/etc. and the same :- defaults),
original_basepath_is_set, preview_num_next_html, preview_num_prev_html,
and tarball_include. Escaping and defaults are unchanged, so rendered
output is byte-identical (verified by diff -r of a full --generate
album, before vs after).
Add test_template_render_var_dispatch_is_extensible proving every
declared kind resolves to a handler and that a newly defined handler is
dispatched without touching the core loop (OCP). just test, just
shellcheck, just check-generated and git diff --check all pass.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
|
source_template_file previously built the BASH_ENV context file by piping
"declare -p ...; declare -f; serialize_template_render_context ...;" into a
fresh "bash -euo pipefail" subprocess for every rendered page. That dumped all
~5000 lines of shuriken functions and spawned a subprocess per page just to run
the serializer - a large per-page cost for albums with hundreds-to-thousands of
pages.
Now serialize_template_render_context runs in the current shell with stdout
redirected straight into the context tempfile, followed by an appended
"unset BASH_ENV". The serializer returns its own non-zero status explicitly so
the failure is detected via an "if" status-test (which returns normally through
the RETURN trap and cleans up the partial context file), robust even when
source_template_file runs inside a status-tested "if template ..." call chain
where bash would otherwise suppress an inner errexit abort.
The trap-based cleanup (RETURN plus INT/TERM/HUP re-raising to $BASHPID) is
preserved unchanged. Two serializer test mocks that relied on the removed
"| bash" errexit now return non-zero explicitly. Rendered HTML output is
unchanged; just test, just shellcheck, just check-generated and
git diff --check all pass.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
|
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>
|
|
The details view already showed a title="" hover summary (camera, aperture,
ISO, shutter, date) on its image; the normal image view did not. Wire the
same tooltip into view.tmpl:
- render_view_page now computes the EXIF tooltip via photo_exif_tooltip_text
(reusing the shared identify cache, so no extra ImageMagick work) and passes
it as the exif_tooltip context var.
- exif_tooltip is now a required render field for the view template too.
- view.tmpl adds the title="" attribute to its <img> when the summary is
non-empty, mirroring details.tmpl (no title when there is no EXIF).
- Tests: extend the EXIF render test to assert the view image carries the same
tooltip, and update the required-context-vars expectation for view.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
|
Final integration of the stats-site feature:
- Add STATS_PAGE config (default yes) mirroring SPLASH_PAGE through every
layer: shuriken.default.conf, apply_config_defaults, CLI_OPTION_SPEC
(--stats/--no-stats), the CLI_CONFIG_OVERRIDE_TARGETS allowlist (without
which the flag was silently ignored), usage text, yes/no validation,
print_config, the action config list + effective-setting log, the
generation metadata, and the dry-run plan.
- generate() now calls a gated generate_stats_pages helper after the album
pages (and before archiving, so the stats/camera pages are tarballed):
collect_photo_exif_stats + render_stats_page . . + render_camera_pages . .
- Emit "stats_page" in shuriken.json next to "splash_page".
- Gate the header Stats nav link behind STATS_PAGE via a new
render_stats_page_html config_html field, so it is hidden (no 404) when
stats are disabled.
- Document STATS_PAGE and --stats/--no-stats in the README.
- Tests: full --generate produces stats.html + camera-<slug>.html + the nav
link and stats_page=true; --no-stats suppresses all three and sets
stats_page=false; print-config and metadata expectations updated.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
|
Implement render_camera_pages <html_dir> <backhref> in stats.source.sh: one
camera-<slug>.html per camera in STATS_CAMERA_SLUGS, reusing the aggregation's
collision-resolved slugs and per-camera photo lists so filenames match pm0's
leaderboard links. Each page reuses the shared header/footer chrome plus a new
camera.tmpl that renders an HTML-escaped camera heading, a pre-built thumbnail
grid, and a back-to-stats link.
Thumbnails reuse preview.tmpl's markup style (thumb image from thumbs/) but link
to the full-size image under photos/ rather than a per-photo view page, since the
view-page name is not derivable outside the album pagination loop. The photos and
thumbs dir names are fixed to match generate()'s render_album_pages call.
Cameras are walked in LC_ALL=C label order for reproducible output, and cameras
with no recorded photos are skipped. Adds the camera_name/camera_thumbs render
field specs and a tests/cli.sh case covering per-camera files, slug collisions,
HTML escaping, thumbnail link targets, and determinism.
This provides the function + template + tests only; wiring into generate() and
the STATS_PAGE toggle remain for rm0.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
|
Render the aggregated STATS_* counters from the om0 module into a static
stats.html. render_stats_page builds the variable-length body (camera
leaderboard, temporal/exposure/dimension histograms, format and enum
breakdowns) as HTML and hands it to the new stats.tmpl through a raw
context field, wrapping it with the shared header/footer the way
view/details pages do. Bars are pure CSS so the page stays JavaScript-free.
- new template share/templates/default/stats.tmpl (page chrome + body)
- render_stats_page <html_dir> <backhref> [page_name] plus small section
builders in src/lib/stats.source.sh; EXIF-derived labels are HTML-escaped
- new TEMPLATE_RENDER_FIELD_SPECS field render_stats_body_html (context_raw)
and 'stats' added to render_backhref_html's required list
- camera leaderboard entries link to camera-<slug>.html (um0 owns those)
- Stats nav link wired into the shared header bar
- tests cover leaderboard links, counts/percentages, a histogram section,
&/< escaping, and omission of empty categories
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
|
The signal handler in source_template_file re-raised the caught signal
with `kill -s $sig $$`. Because the function commonly runs inside
backgrounded render subshells (queue_album_view_render_job), $$ expands
to the main shuriken PID, not the subshell's own. On interrupt the
handler therefore signalled the main shell -- which had already cleared
its own staging traps -- killing it mid-cleanup (leaking the staging
dir) instead of terminating just the render subshell.
Re-raise to $BASHPID, the current (sub)shell's real PID, which equals $$
in the foreground case so it is correct everywhere. Extend
test_template_interrupt_removes_context_file to run the render in a
backgrounded subshell and assert the parent shell survives the re-raise;
verified it fails against a $$-based build and passes with $BASHPID.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
|
source_template_file created its context tempfile with mktemp but only ran
rm -f on the success and explicit-failure paths. When a render was interrupted
by a signal (terminate_active_generation SIGTERMs the backgrounded render
subtree on Ctrl-C/abort) the tempfile leaked.
Register cleanup in exactly one place, inline in source_template_file's body:
- A RETURN trap covers normal and error returns and clears all of these traps
(including itself) so it cannot linger and fire on an enclosing function's
return against the out-of-scope context_file local (set -u). The trap must be
set in the function body, not a helper: without functrace a RETURN trap is not
function-scoped and would fire when a helper returns, deleting the file before
the render runs.
- INT/TERM/HUP handlers remove the file, clear the traps and re-raise so the
process still exits with the signal's default disposition. PIPE is not
trapped (the internal context-build pipeline emits legitimate SIGPIPE);
SIGKILL is untrappable, leaving only the OS-reaped KILL-escalation case.
The redundant rm -f calls are removed. Adds
test_template_interrupt_removes_context_file, which runs an in-flight render and
SIGTERMs it, asserting the context file is gone.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|