~/changelog · currently v68
changelog
Commits to this site, newest first. Versions auto-increment per push. Co-authored with claude.
-
backfill: webp + lqip on all 95 existing images
-
webp + lqip pipeline, broken-image checker, link checker, dev watch
details
images: - process-staging.mjs now renders 6 keys per upload: jpeg + webp for full/med/thumb. metadata.yaml entries get s3_webp/s3_med_webp/ s3_thumb_webp/lqip in addition to the existing jpeg urls. - makeLQIP renders a 24px-wide blurred jpeg and embeds it as base64 (~600 bytes per image) in metadata as `lqip`. - new helper `pictureFor(im, size, alt, extraAttrs)` in build.mjs: emits
with WebP source + JPEG fallback + inline lqip background-image when available. - photo page main image, gallery feed view, and post-inline images all use pictureFor (or its inline equivalent in the markdown body rewriter). older entries without webp variants fall through to a plain . new scripts: - scripts/backfill-webp-lqip.mjs: walks all metadata + post frontmatter, downloads each S3 jpeg that's missing webp, renders + uploads variants, writes lqip base64, patches the yaml/md. idempotent. - scripts/check-images.mjs (`npm run check:images`): HEADs every S3 url referenced from content. exit 1 if any are unreachable. - scripts/check-links.mjs (`npm run check:links`): crawls _site html and HEADs every external href. opt-in, run after build. - scripts/dev.mjs (`npm run dev`): watches content/templates/ design/copy.yaml + scripts/build.mjs; rebuilds on change; serves _site on :4321.
-
perf + a11y + feeds + new pages: 8 enhancements
details
performance: - preconnect to s3 bucket in - width/height attrs on every
(uses exif dims already in metadata) — eliminates layout shift as photos load - fetchpriority="high" on the photo-page hero accessibility: - prefers-reduced-motion: kills hover-grow transforms + cuts transition durations to 0.01ms for users who set the preference - :focus-visible: 2px accent outline + accent-3 link bg - skip-to-content link (visible on focus) feeds + favicon: - /atom.xml alongside /rss.xml (some readers prefer atom) - for both feeds in head - design/favicon.svg: simple ~/red monogram, wired in head + copied to _site at build new pages: - /musings/tags index — alphabetical list of all tags + counts - /musings/YYYY year archives — group posts by year, with cross- links to other years - related-posts section on each post page (up to 3 by shared-tag score, then recency) random-post link: - "take me somewhere" link in the footer; clicking picks a random musing url client-side. all post urls are emitted as a json attribute on the link.
-
gallery view modes: folders / grid / feed
details
each gallery page that has sub-albums now renders a small toggle (top-right of the gallery) with three views: - folders (default): sub-albums above, then loose photos in this folder below - grid: every image in this subtree (this album + every descendant) in a single tight VSCO grid - feed: same set, but as a vertical scrolling stream — large centered images with caption + album link below each implementation: - build.mjs computes `allDescendantImages` (deduped by src) per node - template renders all three modes; CSS toggles which is visible via :checked sibling-selector — no JS required for the toggle - a tiny JS hook persists the chosen mode in localStorage so it survives page navigation - empty galleries (no sub-albums, no photos) fall through to the empty-state message, and single-folder galleries skip the toggle.
-
license page + footer copyright + changelog redesign + mona/outback fixes
details
- /license page: full CC BY-NC 4.0 reuse terms with body + bullets + commercial section, all driven by copy.license in copy.yaml. - footer adds a 'license' link, an inline copyright line '© George Main · CC BY-NC 4.0', drops the 'est
' line. - changelog: cleaner ol-based row layout, version+date+hash inline at top (hash is a clickable github.com/.../commit/ link), subject on its own line, optional body collapsed in a summary. - outback restructure: misc is no longer a sub-album. All 19 misc photos moved (S3 mv) into /media/australia/outback/ as loose photos. content/media/australia/outback/misc/ deleted. - new content/media/australia/tasmania/metadata.yaml: cover = russell-falls (better than the default first-descendant). - mona cover = fat-car (more striking than cement-truck for the album thumbnail). - mona alts: noodles-on-pedestal renamed to rubber-bands-on-pedestal (s3 mv + alt); 'red text piece by an artist commenting on artists' → 'Red text piece, MONA.'; bit.fall flipped to 'looking down at'; 'Textural close-up resembling stalactites' → 'Wall of rock inside MONA.' -
album cover overrides + corrections + photo-arrow layout
details
- build.mjs: new optional `cover:` field on a gallery's metadata.yaml selects which image becomes the album cover (instead of images[0]). cover lookup recurses into descendant galleries so virtual parents (e.g. /media/australia, /media/australia/outback) can pick a cover from any nested album. - new content/media/australia/metadata.yaml + .../outback/metadata.yaml (parent nodes that were previously virtual-only) — both pick uluru-sunset as cover. - misc covered by northern-territory-sign; kings-canyon by lost-city-overview. - s3 renames + alt fixes for the misc outback gallery: memorial-bell → centre-of-australia (it's the Centre of Australia cast-iron monument); red-dirt-campground → yulara-coach-campground; desert-camp → yulara-camp-overview; glamping-tents-overview → yulara-eco-tents-overview; camp-from-above → yulara-camp-from-above; glamping-tents → yulara-eco-tents. - kings-canyon: overhang → rim-walk-squeeze (alt: rim walk squeeze between two rocks); person-on-rim → canyon-rim-trail (no person in shot); shelby-on-rim deleted entirely. - uluru: uluru-from-viewpoint deleted. - photo page < / > arrows moved beside the image (flex row) instead of overlaid on top of it.
-
uluru: 'red-dirt-tire-tracks' was actually shoe prints in sand — rename slug + s3 keys + alt
-
revert palette to v2 cream — fully back to v2 design
-
homepage shuffle + perf + universal hover-grow
details
- drop the musings index subtitle ("Long-form notes...") — not needed. copy.musings.index.intro removed from copy.yaml and template. - fix random-photo shuffle: allImages was including the same image twice when it appeared in both a real and a synthesized-from-post gallery. dedupe by src URL so consecutive shuffles always land on a different image. - preload all shuffled photos via new Image() at homepage load so reroll swaps are instant from the browser cache. - hover-grow added to: album cards (.sp-album), homepage random-photo widget (#photo-img), photo-page main image. previously only the vsco grid cells and post figures had it. -
revert v3 design refresh — keep paper palette only
details
user feedback: v3 paper-card design felt worse (abrupt bottom edge, broke the stats grid boxes). preferred the v2 chrome but liked the new paper color. so revert tokens.css + site.css + templates/ to commit 6ce9249 (v2-final), then patch in only the cool paper palette (#f2f1ec bg, #b21f1a accent) from v3. what stays from v3: just the color palette. what's restored from v2: section markers, paper-card-free shell, stats-grid boxes, all template structure.
-
v3 design refresh: paper palette, less power-user chrome
details
design bundle update from claude design tool: - new tokens.css: cool off-white paper (#f2f1ec), no beige. deeper burgundy red accent (#b21f1a). subtle SVG noise texture. gutter 40px. - new site.css: content sits on a paper card (.site-shell now has background + border-left/right). section markers are now a centered '· · ·' ornament + small italic '— a random musing —' kicker, not '// 01 · words' mono captions. headings are serif weight 600, not mono. tags are serif italic. .sp-tree replaced by nested ul. new .sp-photo-frame + .sp-photo-meta + .sp-photo-counter classes. - templates: - drop mono '~/musings · 67 posts' breadcrumbs everywhere. - musing/category/musings-index now share the .musings-layout with a tree sidebar (was: index didn't have the tree). class renamed musings-main -> musings-body. - serif italic crumbs with › separators (Musings › Travel › Japan). - written-out dates (May 11, 2026) instead of 2026·05·11. - drop file-path lines from random widgets — title is the title. - photo page switches table → -
photo page: floating < / > arrows on either side of the image
-
upload mt field national park gallery (russell, horseshoe, lady barron falls)
details
5 photos from sony rx100 vii, ~20mb of originals on s3, ~0 bytes in repo. metadata.yaml at content/media/australia/tasmania/mt-field/ with full camera/lens/settings + dimensions captured from sharp. also: - alt text is now optional with a warning, not a hard build failure (build.mjs + process-staging.mjs) - build auto-creates virtual parent gallery nodes for any folder depth, so /media/australia and /media/australia/tasmania resolve as routes even without metadata.yaml at those levels
-
backfill image dimensions; capture w/h via sharp on every ingest going forward
-
photo page: add size, dimensions + megapixels rows
-
cap image heights so portrait shots fit the viewport without forcing scroll
details
- homepage random photo: max-height 60vh - photo page: max-height 82vh, width auto - post figures full-width: max-height 75vh with object-fit contain - inline-floated images keep natural sizing
-
fix: featured-image entry now picks up s3 urls from frontmatter.images map
-
all images on s3 — zero image bytes in repo (migration of existing posts done)
-
stats: pictures-size pulls recorded full-size bytes (s3 original) when available
-
drop accidentally-committed AWSCLIV2.pkg installer
-
s3 originals + camera EXIF metadata
details
scripts/process-staging.mjs: - reads full EXIF (camera, lens, ISO, aperture, shutter, focal length, dimensions) in addition to date/GPS - scrubs the original (strips all EXIF including GPS via sharp's default behavior) - uploads scrubbed full-size original to s3://georgemain-com-media/
/ - writes a smaller 1400px local reference into content/media/ for build-time thumb/med generation (keeps repo lean) - metadata.yaml entries now carry { file, s3, alt, title, date, location, exif } templates/media-photo.eta: - camera/lens/settings/dimensions table when exif present - "view full size ↗" link to the S3 original when s3 is set, falls back to local file for older entries -
homepage: by-the-numbers stats grid (musings/words/pictures/size/dates)
-
japan: fix year, 2023 not 2024
-
category page: replace .sp-tree (white-space: pre eats template whitespace) with compact .cat-list
-
japan: add Kiyomizu-dera title image
-
japan: replace vending machine photo with higher-res original
-
dates: budapest 2025-11-01, japan 2024-09-01
-
cleanup: wire home button copy keys, strip orphan token-viewer css, better build error messages
details
- templates/home.eta: random/next/read/open buttons now read their labels from copy.yaml's home.random_musing.* and home.random_photo.*. previously hardcoded. - design/site.css: dropped the .tg-* class block (~64 lines). these were styles for the token-viewer page in the design canvas, never used in production. - scripts/build.mjs: error messages now lead with the file path and name the exact missing field — e.g. "
: image 'foo.jpg' is missing required 'alt' text" instead of "Image alt missing in ...". -
footer: simpler creed, drop em-dash
-
GBR post: hero caption + boats-my-whole-life note
-
per-photo pages with prev/next nav (vsco-style)
details
clicking a gallery cell now opens a dedicated photo page at /media/
/ / instead of the raw image file. each page: - big image with caption (alt text) - metadata table: title, date, where, album, original file link - prev / next nav (wraps around at the ends) - counter "3 / 12" - left/right arrow keys also navigate - ↑ back to link works for both real galleries and synthesized-from-musing galleries. -
auto-derive media gallery path for any musing with images
details
- before: synthesis was opt-in via gallery.path - now: any post with images auto-gets a gallery. default path = post category minus first segment + slug. override with gallery.path, disable with gallery: false. - trim verbose alt text on three images - make nunobiki falls inline (small) instead of full-width - add CLAUDE.md
-
tree sidebar: nested ul (wraps long names) + mobile-collapsed by default
details
- renderTreeHtml outputs nested
- so long names like
"great-barrier-reef" wrap naturally instead of being clipped by the
fixed sidebar column.
- depth indent via padding-left + dashed left-border guide (cleaner than
ASCII glyphs that depend on white-space: pre).
- mobile (<=900px): tree is hidden behind a "browse ▾" toggle (pure CSS
checkbox hack). desktop unaffected — tree always visible.
- previous spaces+pre approach is gone; no more clipping or weird wrap.
also fixed earlier issue: vsco gallery captions now appear BELOW the
cell on hover, not overlaid on the photo.
-
fix: stripe pattern bleeding through over real photos in gallery cells
-
synthesize media galleries from musings' frontmatter
details
opt-in via gallery: { path, title, subtitle, location, date } on a musing. build walks ancestor segments to create virtual parent gallery nodes so breadcrumbs and album-tree work. images stay single-source in the post's media/ folder (no duplication). synthesized gallery + each card show a "↩ from the musing:" link back to the source post. skip the page-route check for image/static asset urls. -
fix: don't double-escape alt/caption; trim hero caption
-
post figures: scale only when hovering the image itself, not the caption
-
post figures: subtle hover-enlarge to match media grid
-
fix: marked v14 image renderer signature — hero figures were rendering with undefined href
-
constrain post figure images to figure width
-
fix: dedupe featured_image key in great barrier reef post
-
great barrier reef: add hero photo + inline deck shot
-
decode html entities when stripping markdown for previews
-
add folder-tree sidebar to musing posts + tighten category page
details
- build.mjs: walks musings into a {folder|post} tree, renders to HTML with active/in-active classes. Passed to musing.eta + musings-category.eta. - musing.eta + musings-category.eta: 2-col grid (200px tree + reading column). - site.css: .musings-layout grid, sticky tree sidebar, :has() override widens the site-shell to 1080px on musing pages so the tree fits without squishing the reading column. Mobile collapses to single column under 900px. - category page restructured: no more 3 stacked sp-sections each with 64px top margin + horizontal rule. Now subfolders + posts are tight sequential blocks with just a thin rule + // caption between them. -
drop top gap+rule above the first section on a page
-
fix: random/next buttons — eta 3 doesn't recognize <%- so JSON payload was empty
-
apply v2 design refresh — toned-down retro
details
design bundle update from claude design tool: - flat background (drop grid texture). content column narrowed 1240→820. - new tokens.css: red-only, ibm plex only, fs ramp adjusted, lh-body=1.65, reading=680px / content=760px / max=820px / gutter=32px. - new site.css: section markers are now thin top rule + "// 01 · words" caption (was the boxy [§ ...] rail). random-musing + random-photo blocks drop the panel chrome entirely (sp-panel → sp-block). tags become plain #tag text (no border/chip). lighter buttons, no offset shadow. new sp-link-btn ([text] inline link-style button). albums grid now 2-col. - templates updated to match: home is content-first with sp-block + sp-link-btn, musings index uses inline browse strip (folder list + tag chips) instead of right sidebar, footer uses sp-footer-mark class and inlines version + est.
-
changelog: render from git log instead of manual yaml entries
-
footer: keep version label, drop attribution
-
remove footer attribution line entirely
-
fix html/body sizing — use 100vh on body, drop 100% on html
-
remove stale hero comment from copy.yaml
-
remove homepage hero — straight into the random-musing widget
-
fix scroll clipping on long pages + relabel footer attribution
details
- design/site.css: remove overflow:hidden from .site-page. it was there for design-canvas artboards but clips body scroll on the real site. long posts now scroll. - copy.yaml: footer.built_by_hand "built by hand" -> "built with claude". truthful and matches the changelog tone (prose hand-written, finished with AI).
-
add staging/ media ingest workflow + EXIF GPS check in pre-commit
details
- scripts/process-staging.mjs: interactive CLI (npm run media) - reads EXIF date/GPS via exifr - groups bursts (same filename prefix + capture time within 30 min) - asks gallery / batch date / batch location once per burst - per-image: title (optional) + alt (required) - strips ALL metadata on output (sharp default behavior) - moves file out of staging/ into content/media/
/ - appends entry to metadata.yaml - offers img_NNN renaming - scripts/check-pii.mjs now also scans staged image EXIF and fails on GPS coords, so missed scrubs are caught at commit time. - .claude/skills/process-staging/SKILL.md as the chat-friendly entry point. Co-Authored-By: Claude Opus 4.7 -
add CI deploy, PII pre-commit hook, claude code skills
details
- .github/workflows/deploy.yml: build + deploy to GitHub Pages on push to main - .githooks/pre-commit + scripts/check-pii.mjs: regex scan of staged diff for emails, phones, SSNs, CC#s, GPS coords, precise timestamps, US addresses. optional AI second-pass when ANTHROPIC_API_KEY is set. .pii-allowlist.yaml lets known-public strings through. - npm run setup wires git hooks (one-time per clone). - .claude/skills/: add-musing, add-images-to-musing, add-media-gallery, add-images-to-gallery, add-tab, seo-audit, add-static-site, pii-audit. Co-Authored-By: Claude Opus 4.7
-
add static site generator + copy.yaml + initial musings
details
build pipeline: node + eta templates + sharp for images. content lives in content/musings (markdown + frontmatter) and content/media (yaml + jpegs). all human-facing copy is in copy.yaml so it can be edited in one place. version auto-increments from git commit count. light/dark toggle persists to localStorage. existing flight pages moved to content/sites/ and pass through to /sites/* unchanged. Co-Authored-By: Claude Opus 4.7
-
update timing and add pilot stats
-
update for kml
-
add cache
-
add flight viewer
-
add desktop version
-
mobile
-
add first-flight
-
Create CNAME
-
Delete CNAME
-
Create CNAME
-
Initial commit