summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2026-06-11 10:43:19 +0300
committerPaul Buetow <paul@buetow.org>2026-06-11 10:43:19 +0300
commit1e7b394e56085a9cc33b8b8b64b094d5223e8853 (patch)
treed716db2d332904bb66f34572ec066d19170cd9cc
parentcbd3cf2be4ce50092ec4eb3f4b28d77c0aa044e4 (diff)
Add shuriken logo and generated favicon
-rw-r--r--Justfile2
-rw-r--r--README.md12
-rw-r--r--assets/docs/shuriken-logo.jpgbin0 -> 50976 bytes
-rw-r--r--assets/docs/shuriken-logo.svg18
-rw-r--r--assets/site/favicon.icobin0 -> 123870 bytes
-rwxr-xr-xbin/photoalbum43
-rw-r--r--share/templates/default/header.tmpl1
-rw-r--r--share/templates/default/redirect.tmpl1
-rw-r--r--share/templates/default/splash.tmpl1
-rw-r--r--src/lib/album.source.sh22
-rw-r--r--src/lib/bootstrap.source.sh18
-rwxr-xr-xsrc/photoalbum.sh3
-rwxr-xr-xtests/cli.sh12
13 files changed, 129 insertions, 4 deletions
diff --git a/Justfile b/Justfile
index e89c783..3e58955 100644
--- a/Justfile
+++ b/Justfile
@@ -140,6 +140,8 @@ install: check-generated build
install -d "{{DESTDIR}}{{DATADIR}}/{{NAME}}"
rm -rf "{{DESTDIR}}{{DATADIR}}/{{NAME}}/templates"
cp -R ./share/templates "{{DESTDIR}}{{DATADIR}}/{{NAME}}/"
+ rm -rf "{{DESTDIR}}{{DATADIR}}/{{NAME}}/assets"
+ cp -R ./assets/site "{{DESTDIR}}{{DATADIR}}/{{NAME}}/assets"
install -d "{{DESTDIR}}{{SYSCONFDIR}}"
install -m 0644 \
./src/photoalbum.default.conf \
diff --git a/README.md b/README.md
index 31482e6..2918b5f 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,7 @@
# photoalbum
+![Shuriken logo](assets/docs/shuriken-logo.svg)
+
photoalbum is a minimal Bash script for Unix like operating systems (such as Linux) to generate static web photo albums.
The resulting static photo album is pure HTML+CSS (without any JavaScript!).
@@ -20,10 +22,10 @@ changing either file, and use `just check-generated` to verify that the tracked
script has not drifted. `just test` and `just install` run that drift check
before rebuilding so stale committed output is not hidden.
-`just install` installs `photoalbum` to `/usr/bin`, templates to
-`/usr/share/photoalbum/templates`, and the default config to
-`/etc/default/photoalbum`. Override paths with `DESTDIR`, `PREFIX`, `BINDIR`,
-`DATADIR`, or `SYSCONFDIR` when packaging or staging an install:
+`just install` installs `photoalbum` to `/usr/bin`, templates and static assets
+to `/usr/share/photoalbum`, and the default config to `/etc/default/photoalbum`.
+Override paths with `DESTDIR`, `PREFIX`, `BINDIR`, `DATADIR`, or `SYSCONFDIR`
+when packaging or staging an install:
```
DESTDIR="$PWD/pkg" PREFIX=/usr just install
@@ -106,6 +108,8 @@ Successful generation writes `photoalbum.json` into the output directory. This
metadata records the generator version and timestamp, config source, template
directory, supported source image and generated file counts, tarball status, and
effective settings useful for debugging a published album.
+Generation also writes `favicon.ico` into the output directory and the default
+templates link to it.
Normal generation preserves reusable generated artifacts from the previous
`DIST_DIR` while still rerendering HTML, random splash/background choices,
diff --git a/assets/docs/shuriken-logo.jpg b/assets/docs/shuriken-logo.jpg
new file mode 100644
index 0000000..fc7216c
--- /dev/null
+++ b/assets/docs/shuriken-logo.jpg
Binary files differ
diff --git a/assets/docs/shuriken-logo.svg b/assets/docs/shuriken-logo.svg
new file mode 100644
index 0000000..58ed8ab
--- /dev/null
+++ b/assets/docs/shuriken-logo.svg
@@ -0,0 +1,18 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" role="img" aria-labelledby="title desc">
+ <title id="title">Shuriken logo</title>
+ <desc id="desc">A four-point shuriken mark with a red center ring.</desc>
+ <rect width="256" height="256" rx="36" fill="#f4f0e8"/>
+ <circle cx="128" cy="128" r="108" fill="#18212b"/>
+ <path
+ d="M128 18 153 94 231 65 190 128 231 191 153 162 128 238 103 162 25 191 66 128 25 65 103 94Z"
+ fill="#dce5eb"
+ stroke="#0b1118"
+ stroke-linejoin="round"
+ stroke-width="10"/>
+ <path
+ d="M128 43 146 101 204 82 171 128 204 174 146 155 128 213 110 155 52 174 85 128 52 82 110 101Z"
+ fill="#7f8e99"/>
+ <circle cx="128" cy="128" r="39" fill="#18212b"/>
+ <circle cx="128" cy="128" r="24" fill="none" stroke="#c7432b" stroke-width="10"/>
+ <circle cx="128" cy="128" r="8" fill="#f4f0e8"/>
+</svg>
diff --git a/assets/site/favicon.ico b/assets/site/favicon.ico
new file mode 100644
index 0000000..754a880
--- /dev/null
+++ b/assets/site/favicon.ico
Binary files differ
diff --git a/bin/photoalbum b/bin/photoalbum
index 33f369a..065793f 100755
--- a/bin/photoalbum
+++ b/bin/photoalbum
@@ -7,8 +7,11 @@ set -euo pipefail
declare -r VERSION='0.7.0'
declare -r DEFAULTRC="${PHOTOALBUM_DEFAULT_RC:-/etc/default/photoalbum}"
declare -r PACKAGED_TEMPLATE_DIR='/usr/share/photoalbum/templates/default'
+declare -r PACKAGED_ASSET_DIR='/usr/share/photoalbum/assets'
DEFAULT_TEMPLATE_DIR="${PHOTOALBUM_DEFAULT_TEMPLATE_DIR:-$PACKAGED_TEMPLATE_DIR}"
declare -r DEFAULT_TEMPLATE_DIR
+DEFAULT_ASSET_DIR="${PHOTOALBUM_DEFAULT_ASSET_DIR:-$PACKAGED_ASSET_DIR}"
+declare -r DEFAULT_ASSET_DIR
PHOTOALBUM_OUTPUT_MODE="${PHOTOALBUM_OUTPUT_MODE:-normal}"
PHOTOALBUM_ACTIVE_GENERATION_PID=''
PHOTOALBUM_FORCE_GENERATE="${PHOTOALBUM_FORCE_GENERATE:-no}"
@@ -221,6 +224,24 @@ resolve_default_template_dir() {
printf '%s\n' "$DEFAULT_TEMPLATE_DIR"
}
+resolve_default_asset_dir() {
+ local source_root
+
+ if [ -d "$DEFAULT_ASSET_DIR" ]; then
+ printf '%s\n' "$DEFAULT_ASSET_DIR"
+ return
+ fi
+
+ source_root=$(resolve_source_root)
+
+ if [ -n "$source_root" ]; then
+ printf '%s\n' "$source_root/assets/site"
+ return
+ fi
+
+ printf '%s\n' "$DEFAULT_ASSET_DIR"
+}
+
template_dir_uses_default() {
if [ -z "${TEMPLATE_DIR+x}" ] || [ -z "$TEMPLATE_DIR" ]; then
return 1
@@ -2188,6 +2209,26 @@ prepare_generation_photo_assets() {
create_all_photo_derivatives 'photos' 'thumbs' 'blurs'
}
+copy_site_favicon() {
+ local asset_dir
+ local favicon_src
+
+ asset_dir=$(resolve_default_asset_dir)
+ favicon_src="$asset_dir/favicon.ico"
+
+ if [ ! -r "$favicon_src" ]; then
+ config_error "favicon file $favicon_src must be readable"
+ return 1
+ fi
+
+ log_verbose "Copying favicon to $(_display_path "$DIST_DIR/favicon.ico")"
+ cp "$favicon_src" "$DIST_DIR/favicon.ico"
+}
+
+prepare_generation_site_assets() {
+ copy_site_favicon
+}
+
clear_rendered_html() {
find "$DIST_DIR" -type f -name '*.html' -delete
}
@@ -2213,6 +2254,7 @@ generate() {
fi
prepare_generation_photo_assets
+ prepare_generation_site_assets
clear_rendered_html
render_album_pages 'photos' '.' 'thumbs' 'blurs' '.' "$tarball_name"
create_generation_archive "$tarball_name"
@@ -2286,6 +2328,7 @@ dry_run() {
printf ' %s/index.html (%s album index redirect)\n' \
"$DIST_DIR" "$html_index_count"
fi
+ printf ' %s/favicon.ico\n' "$DIST_DIR"
printf ' %s/photoalbum.json\n' "$DIST_DIR"
printf ' %s/photos/* (%s image files)\n' "$DIST_DIR" "$image_count"
printf ' %s/thumbs/* (%s image files)\n' "$DIST_DIR" "$image_count"
diff --git a/share/templates/default/header.tmpl b/share/templates/default/header.tmpl
index 7f30a77..953109a 100644
--- a/share/templates/default/header.tmpl
+++ b/share/templates/default/header.tmpl
@@ -2,6 +2,7 @@ cat <<END
<html>
<head>
<title>${render_title_html}</title>
+ <link rel="icon" href="${render_backhref_html}/favicon.ico" type="image/x-icon">
<style type="text/css">
body {
background-color: #000000;
diff --git a/share/templates/default/redirect.tmpl b/share/templates/default/redirect.tmpl
index 866aa38..b7119aa 100644
--- a/share/templates/default/redirect.tmpl
+++ b/share/templates/default/redirect.tmpl
@@ -1,6 +1,7 @@
cat <<END
<html>
<head>
+ <link rel="icon" href="./favicon.ico" type="image/x-icon">
<meta http-equiv='refresh' content='0; url=${render_redirect_page_html}.html'>
</head>
<body>
diff --git a/share/templates/default/splash.tmpl b/share/templates/default/splash.tmpl
index ce3483d..5363978 100644
--- a/share/templates/default/splash.tmpl
+++ b/share/templates/default/splash.tmpl
@@ -2,6 +2,7 @@ cat <<END
<html>
<head>
<title>${render_title_html}</title>
+ <link rel="icon" href="${render_backhref_html}/favicon.ico" type="image/x-icon">
<style type="text/css">
body {
background-color: #000000;
diff --git a/src/lib/album.source.sh b/src/lib/album.source.sh
index 760201a..0f67396 100644
--- a/src/lib/album.source.sh
+++ b/src/lib/album.source.sh
@@ -890,6 +890,26 @@ prepare_generation_photo_assets() {
create_all_photo_derivatives 'photos' 'thumbs' 'blurs'
}
+copy_site_favicon() {
+ local asset_dir
+ local favicon_src
+
+ asset_dir=$(resolve_default_asset_dir)
+ favicon_src="$asset_dir/favicon.ico"
+
+ if [ ! -r "$favicon_src" ]; then
+ config_error "favicon file $favicon_src must be readable"
+ return 1
+ fi
+
+ log_verbose "Copying favicon to $(_display_path "$DIST_DIR/favicon.ico")"
+ cp "$favicon_src" "$DIST_DIR/favicon.ico"
+}
+
+prepare_generation_site_assets() {
+ copy_site_favicon
+}
+
clear_rendered_html() {
find "$DIST_DIR" -type f -name '*.html' -delete
}
@@ -915,6 +935,7 @@ generate() {
fi
prepare_generation_photo_assets
+ prepare_generation_site_assets
clear_rendered_html
render_album_pages 'photos' '.' 'thumbs' 'blurs' '.' "$tarball_name"
create_generation_archive "$tarball_name"
@@ -988,6 +1009,7 @@ dry_run() {
printf ' %s/index.html (%s album index redirect)\n' \
"$DIST_DIR" "$html_index_count"
fi
+ printf ' %s/favicon.ico\n' "$DIST_DIR"
printf ' %s/photoalbum.json\n' "$DIST_DIR"
printf ' %s/photos/* (%s image files)\n' "$DIST_DIR" "$image_count"
printf ' %s/thumbs/* (%s image files)\n' "$DIST_DIR" "$image_count"
diff --git a/src/lib/bootstrap.source.sh b/src/lib/bootstrap.source.sh
index bb309f9..3e514fc 100644
--- a/src/lib/bootstrap.source.sh
+++ b/src/lib/bootstrap.source.sh
@@ -109,6 +109,24 @@ resolve_default_template_dir() {
printf '%s\n' "$DEFAULT_TEMPLATE_DIR"
}
+resolve_default_asset_dir() {
+ local source_root
+
+ if [ -d "$DEFAULT_ASSET_DIR" ]; then
+ printf '%s\n' "$DEFAULT_ASSET_DIR"
+ return
+ fi
+
+ source_root=$(resolve_source_root)
+
+ if [ -n "$source_root" ]; then
+ printf '%s\n' "$source_root/assets/site"
+ return
+ fi
+
+ printf '%s\n' "$DEFAULT_ASSET_DIR"
+}
+
template_dir_uses_default() {
if [ -z "${TEMPLATE_DIR+x}" ] || [ -z "$TEMPLATE_DIR" ]; then
return 1
diff --git a/src/photoalbum.sh b/src/photoalbum.sh
index 45ace17..b0d3cee 100755
--- a/src/photoalbum.sh
+++ b/src/photoalbum.sh
@@ -7,8 +7,11 @@ set -euo pipefail
declare -r VERSION='PHOTOALBUMVERSION'
declare -r DEFAULTRC="${PHOTOALBUM_DEFAULT_RC:-/etc/default/photoalbum}"
declare -r PACKAGED_TEMPLATE_DIR='/usr/share/photoalbum/templates/default'
+declare -r PACKAGED_ASSET_DIR='/usr/share/photoalbum/assets'
DEFAULT_TEMPLATE_DIR="${PHOTOALBUM_DEFAULT_TEMPLATE_DIR:-$PACKAGED_TEMPLATE_DIR}"
declare -r DEFAULT_TEMPLATE_DIR
+DEFAULT_ASSET_DIR="${PHOTOALBUM_DEFAULT_ASSET_DIR:-$PACKAGED_ASSET_DIR}"
+declare -r DEFAULT_ASSET_DIR
PHOTOALBUM_OUTPUT_MODE="${PHOTOALBUM_OUTPUT_MODE:-normal}"
PHOTOALBUM_ACTIVE_GENERATION_PID=''
PHOTOALBUM_FORCE_GENERATE="${PHOTOALBUM_FORCE_GENERATE:-no}"
diff --git a/tests/cli.sh b/tests/cli.sh
index 53acecf..2a71dba 100755
--- a/tests/cli.sh
+++ b/tests/cli.sh
@@ -239,6 +239,7 @@ test_just_install_and_deinstall_with_destdir() {
test::assert_file_exists "$stage_dir/etc/default/photoalbum"
test::assert_file_exists \
"$stage_dir/usr/share/photoalbum/templates/default/view.tmpl"
+ test::assert_file_exists "$stage_dir/usr/share/photoalbum/assets/favicon.ico"
(
cd "$TEST_REPO_ROOT"
@@ -1657,6 +1658,7 @@ test_dry_run_reports_cli_overrides_without_writes() {
test::assert_contains \
" $dist_dir/index.html (1 album index redirect)" \
"$output"
+ test::assert_contains " $dist_dir/favicon.ico" "$output"
test::assert_contains " $dist_dir/photoalbum.json" "$output"
test::assert_contains " $dist_dir/photos/* (6 image files)" "$output"
test::assert_contains " $dist_dir/thumbs/* (6 image files)" "$output"
@@ -2131,6 +2133,7 @@ test_integration_generates_album_outputs_and_cleans() {
test::assert_file_exists "$TEST_TMPDIR/dist/3-2.html"
test::assert_file_exists "$TEST_TMPDIR/dist/3-2-details.html"
test::assert_file_exists "$TEST_TMPDIR/dist/index.html"
+ test::assert_file_exists "$TEST_TMPDIR/dist/favicon.ico"
test::assert_file_exists "$TEST_TMPDIR/dist/photoalbum.json"
test::assert_no_html_subdir_output "$TEST_TMPDIR/dist"
@@ -2161,6 +2164,12 @@ test_integration_generates_album_outputs_and_cleans() {
test::assert_not_contains 'class="details-photo-link" href="1-2.html"' \
"$details_html"
test::assert_contains '<title>Integration album</title>' "$top_index_html"
+ test::assert_contains \
+ '<link rel="icon" href="./favicon.ico" type="image/x-icon">' \
+ "$top_index_html"
+ test::assert_contains \
+ '<link rel="icon" href="./favicon.ico" type="image/x-icon">' \
+ "$page_html"
test::assert_contains 'Enter album' "$top_index_html"
test::assert_contains 'href="page-1.html"' "$top_index_html"
test::assert_contains \
@@ -2335,6 +2344,9 @@ test_generate_config_no_splash_keeps_index_redirect() {
top_index_html=$(<"$TEST_TMPDIR/dist/index.html")
test::assert_contains 'url=page-1.html' "$top_index_html"
+ test::assert_contains \
+ '<link rel="icon" href="./favicon.ico" type="image/x-icon">' \
+ "$top_index_html"
test::assert_not_contains 'Enter album' "$top_index_html"
test::assert_not_contains '<script' "$top_index_html"
test::assert_not_contains 'javascript:' "$top_index_html"