Commit Graph

124 Commits

Author SHA1 Message Date
svemagie
93972aef35 fix: persist OG image cache outside act runner workspace
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 1m51s
The cache was written to .cache/og/ relative to the workspace, which is
under /usr/local/git/.cache/act/<unique-hash>/hostexecutor/ — a new path
per run, so every build regenerated all images from scratch.

OG_CACHE_DIR env var now controls the cache path (resolved to an absolute
path). CI sets it to /usr/local/git/.cache/og, which survives between runs.
Locally it still defaults to .cache/og inside the project dir.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-01 10:00:53 +02:00
svemagie
cbd5f1b994 fix: og passthrough
Some checks failed
Build & Deploy / build-and-deploy (push) Failing after 20s
2026-03-31 19:54:40 +02:00
svemagie
ba9e64ae9e fix: og image fixes
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 1m16s
2026-03-31 19:24:58 +02:00
svemagie
c40456bcae fix: og-image got lost ... again
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 1m15s
2026-03-31 19:16:45 +02:00
svemagie
219c18138c fix(og): match plain URL slugs; fix Funkwhale GC wipe
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 1m19s
og-fix transform was matching date-based URL segments
(/type/yyyy/MM/dd/slug/) that this site never uses — posts live at
/type/slug/. Every post therefore fell through to the default OG image.

Fixed by updating the regex to /type/slug/index.html and deriving the OG
slug as the bare last URL segment, which matches the filename og.js
already generates. The ogSlug filter is simplified accordingly.

Funkwhale GC bug: gcFunkwhaleImages() deleted the entire image cache
whenever _activeFilenames was empty — which happens if the API returns
valid stats but no listenings with cover URLs. Guard added: GC is
skipped when no images were referenced this build.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-31 14:04:34 +02:00
svemagie
36d9ef1b28 fix: exclude unlisted/private posts from weekly digests
OwnYourSwarm checkin posts (visibility: unlisted) were still appearing
in /digest pages. Add isListed filter to the weeklyDigests collection.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-27 21:10:08 +01:00
svemagie
44a33c0028 fix: sidenotes only appended to article body, not sidebar cards
Step 3b was matching all div.e-content on the page, including sidebar
component cards (prose-sm). Added prose-lg guard so asides are only
inserted into the article body's e-content.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-27 19:27:35 +01:00
svemagie
7263614290 fix: sidenote positioning via data-fn-ref, not parentElement
Browsers re-parent <aside> out of <span class="sidenote-host"> when
parsing (block element inside phrasing content is invalid HTML). This
caused s.parentElement to be .e-content instead of .sidenote-host,
so getBoundingClientRect returned .e-content's top for every sidenote.

Fix: add data-fn-ref="fnrefN" to each <aside class="sidenote"> in the
PostHTML transform. JS looks up document.getElementById(data-fn-ref)
to find the .footnote-ref-num span still inside .sidenote-host, then
measures that element's top for correct vertical alignment.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-27 19:18:25 +01:00
svemagie
9871002232 fix: sidenote JS sets position/overflow directly, uses getBoundingClientRect
Instead of relying on CSS cascade to apply position:relative and overflow
to .e-content/.main-content (which may not fire in time or may be overridden),
the positioning script now sets these inline styles itself. Uses
getBoundingClientRect() subtraction (hRect.top - eRect.top) to measure
position of each .sidenote-host relative to .e-content — viewport-relative
and scroll-invariant. Clears inline styles on resize to < 1440px.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-27 18:57:36 +01:00
svemagie
8f28ee43ec fix: sidenote positioning uses offsetTop, not getBoundingClientRect
getBoundingClientRect() is viewport-relative; subtracting parent.top was
unreliable depending on scroll position at call time. Switch to
h.offsetTop, which gives distance from .sidenote-host to .e-content's
padding edge directly (because .e-content is position:relative = offsetParent).
Also replace DOMContentLoaded listener (already fired at script injection time)
with requestAnimationFrame for immediate post-paint positioning.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-27 18:44:42 +01:00
svemagie
0db48ac6fa fix: sidenotes use absolute positioning in page margin, not padding
Replace float+padding-left approach (which narrowed article text) with
position:absolute on each sidenote, placing it to the right of 100% of
.e-content so it paints in the existing left page margin. Text width is
unchanged. Requires viewport ≥ 1440px where the margin is 224px (210px
needed). JS sets each sidenote's top relative to its reference span via
getBoundingClientRect, with 8px overlap prevention.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-27 18:39:32 +01:00
svemagie
cc9bd8794b fix: sidenote repeated-ref labels, orphan guards, tighter scope 2026-03-27 18:00:03 +01:00
svemagie
40ff45aec8 feat: add sidenotes HTML transform (PostHTML)
Adds a PostHTML transform that converts markdown-it-footnote output into
margin sidenotes. Replaces <sup class="footnote-ref"> with a sidenote-host
span containing a numbered inline marker and a floating <aside>. Also adds
has-sidenotes class to the parent <article> when sidenotes are present.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-27 17:56:01 +01:00
svemagie
0bc2dc3e88 fix: replace eleventy-plugin-footnotes with markdown-it-footnote
Switch from KittyGiraudel's shortcode-based footnotes plugin to
markdown-it-footnote, which handles standard [^1] Markdown syntax
used in Obsidian notes. Remove the {% footnotes %} shortcode from
the post layout as the plugin renders footnotes automatically.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-23 19:32:40 +01:00
svemagie
5354b3349d feat: add eleventy-plugin-footnotes for accessible footnotes
Registers the KittyGiraudel footnotes plugin and adds {% footnotes %}
to post.njk below the article content.

Usage in content:
  {% footnoteref "id" "Footnote text here" %}anchor text{% endfootnoteref %}

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-23 19:08:03 +01:00
svemagie
76a13c4441 fix: normalize related to array in seeAlsoLinks filter
Indiekit stores a single related URL as a plain string, not a YAML list.
Iterating over a string yields characters, breaking the See Also section.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-23 18:53:44 +01:00
svemagie
85beed8ef3 feat: See Also includes in-text links to other blog posts
Adds seeAlsoLinks filter that extracts all internal blog.giersig.eu
links from the raw markdown source and merges them with the explicit
`related` frontmatter field, deduplicated. The section now surfaces
automatically whenever an article links to another post inline.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-23 18:45:58 +01:00
svemagie
2e416ab2e1 feat: See Also and Linked From sections on posts
- `backlinksWith` filter scans raw source files to find posts that
  link to the current post (backlinks), with per-build caching.
- `postByUrl` filter looks up a post by absolute URL for title display.
- post.njk: "See Also" renders resolved `related` URLs with titles;
  "Linked From" lists backlinks computed at build time.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-23 18:13:59 +01:00
svemagie
31831c7e02 feat: celebrate evergreen garden stage promotions
Adds a "Recently Evergreened" section to /garden/ showing posts that
reached evergreen status within the last 90 days, with a dedicated
green celebration card style.

- New `recentEvergreens` Eleventy collection (evergreeSince within 90d)
- garden.njk: conditional celebration section above the stage groups
- tailwind.css: .garden-evergreen-celebration card (evergreen palette)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-23 17:47:46 +01:00
svemagie
f7ce951075 fix(listening): copy Funkwhale images in eleventy.after, not passthrough
Passthrough copy runs before the data cascade, so .cache/funkwhale-images/
is empty when Eleventy processes it. Moving the copy to eleventy.after
guarantees images are downloaded before they're copied to _site/.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-22 07:43:14 +01:00
svemagie
796318e161 feat(listening): cache Funkwhale cover images locally at build time
Wasabi S3 presigned URLs expire after 1 hour, causing broken images on
the listening page. Download cover art at build time, serve from
/images/funkwhale-cache/, and GC any images no longer referenced by
current listening/favorites data.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-22 07:01:54 +01:00
svemagie
20e4403b00 feat(listening): merge Funkwhale and Last.fm into single sorted timeline
Adds a `mergeListens` Eleventy filter that combines both sources into one
array sorted newest-first by timestamp (listenedAt / scrobbledAt). The
Recent Listens section now renders a unified chronological feed with
per-source badges and Alpine filter tabs still working.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-21 16:39:00 +01:00
svemagie
3752e23eaf perf: skip unfurl prefetch when no new interaction URLs (manifest cache)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-19 22:17:26 +01:00
svemagie
9ba7980bc3 perf: memoize hash filter to eliminate redundant disk reads per build
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-19 22:15:30 +01:00
svemagie
39aaf0b79d perf: add build timing instrumentation to OG and unfurl hooks
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-19 22:13:41 +01:00
svemagie
498da21ec0 fix: add robots.txt to Eleventy passthrough copy
Was present in the repo but not copied to _site/ during build,
so it got wiped on every deploy.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-19 15:31:36 +01:00
svemagie
606a7f6b1e fix: use glob patterns for Lora passthrough copy
Individual file → "fonts" mappings were treated as a single output
file path, causing a conflict. Use glob patterns (like Inter) so
Eleventy treats the destination as a directory.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-19 15:02:49 +01:00
svemagie
1ed4cb4663 style: serve Lora locally, lighten bg, bump article font size
- Install @fontsource/lora and serve latin/latin-ext woff2 files
  (weights 400/700, normal + italic) via Eleventy passthrough copy
- Lora now leads the serif font stack in Tailwind and critical CSS,
  with Iowan Old Style / Palatino as system-font fallbacks
- Light-mode background lightened: #fbf1c7 → #fefcf0 (still warm,
  noticeably less yellow)
- Article prose bumped: post.njk prose-lg, page.njk prose-xl

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-19 14:59:03 +01:00
svemagie
66414d8cd6 style: adopt Gruvbox-inspired palette and serif typography
Inspired by brennan.day — warm cream backgrounds (#fbf1c7),
Iowan Old Style/Palatino serif font, Gruvbox blue accent (#076678),
and matching code syntax theme in both light and dark modes.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-19 14:33:58 +01:00
svemagie
d9ac9bffc5 feat: add soft-delete filter and content-warning support
Filter posts with `deleted: true` from all collections so soft-deleted
posts no longer appear on the blog. Add content-warning support: on
listing pages, CW posts show a warning label instead of content; on
single post pages, content is wrapped in a collapsible <details>.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-19 00:59:19 +01:00
svemagie
07369d9687 fix(ai): remove unused kebab-case ai-text-level/ai-code-level fallbacks
The kebab-case keys were never reachable from Nunjucks templates anyway;
camelCase and snake_case cover all actual frontmatter variants.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-17 08:00:24 +01:00
svemagie
571ecb6e40 fix(webmentions): filter out self-interactions from own Bluesky account
Exclude webmentions from svemagie.bsky.social / did:plc:g4utqyolpyb5zpwwodmm3hht
at both build-time (eleventy filter) and client-side (webmentions.js).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-16 00:59:40 +01:00
svemagie
a166af2306 chore: sync upstream — performance, webmentions v2, OG v3
- _data: switch to cachedFetch wrapper (10s timeout + 4h watch cache)
- js/webmentions.js: owner reply threading, platform provenance badges, DOM dedup, Micropub reply support
- js/comments.js: owner detection, reply system, Alpine.store integration
- _includes/components/webmentions.njk: data-wm-* attrs, provenance badge slots, reply buttons
- _includes/components/comments.njk: owner-aware comment form, threaded replies
- widgets/toc.njk: Alpine.js tocScanner upgrade (replaces is-land/inline-JS)
- lib/og.js + og-cli.js: OG card v3 (light theme, avatar, batched spawn, DESIGN_VERSION=3)
- eleventy.config.js: hasOgImage cache, memoized date filters, batched OG/unfurl, post-build GC, YouTube check opt
- base.njk: Inter font preloads + toc-scanner.js script
- critical.css: font-face declarations (font-display:optional)
- tailwind.css: font-display swap→optional
- tailwind.config.js: prose link colors -700→-600
- Color design system: accent-700/300 → accent-600/400 across components

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-15 23:56:56 +01:00
svemagie
ba1dba4661 feat: integrate eleventy-plugin-mermaid for diagram support
Adds @kevingimbel/eleventy-plugin-mermaid so mermaid fenced code blocks
render as interactive diagrams in the browser.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-15 13:01:01 +01:00
svemagie
e8ba3b9ae6 feat: nested tags (Obsidian-style) for categories system
Adds hierarchical tag support using "/" separator (e.g. "tech/programming/js").
- New filters: nestedSlugify, categoryMatches, categoryBreadcrumb,
  categoryGroupByRoot, categoryDirectChildren
- categories collection auto-generates ancestor pages for nested tags
- categories.njk: breadcrumb nav, sub-tags section, ancestor-aware post matching
- categories-index.njk: grouped tree view (root + indented children)
- categories widget: shows root tags only with child count badge
- All category links updated from slugify → nestedSlugify (backward-compatible)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-15 10:56:34 +01:00
svemagie
04b6ed1217 feat: derive gardenStage from nested tags (garden/cultivate)
- eleventyComputed in content.11tydata.js resolves gardenStage from
  category/tags at build time — no explicit gardenStage frontmatter needed
- withoutGardenTags filter strips garden/* from category pill rendering
- categories collection excludes garden/* entries (no phantom category pages)
- All list templates and post layout use withoutGardenTags filter
2026-03-15 09:41:18 +01:00
svemagie
5259509a3c feat: add evergreen garden stage (🌳)
Inspired by Maggie Appleton / Andy Matuschak — evergreen notes are
mature and reasonably complete but still alive and growing.
Sits between cultivate and question in the stage order.
Rendered in teal to distinguish from the green seedling/growing stages.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-15 08:45:09 +01:00
svemagie
48da3404ea feat: Garden dev 0.1 2026-03-14 16:53:31 +01:00
svemagie
448534799a fix: hide private visibility posts from overview collections
Extend isListed helper to exclude both unlisted and private visibility,
so "where" check-in notes (tagged where, used for /where and /been) no
longer appear in listedNotes, listedPosts, or excludeUnlistedPosts.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-12 12:21:52 +01:00
svemagie
9088f3b01d Revert "fix: hide private and where/Loc notes from public overviews and collections"
This reverts commit f04c48e3cb.
2026-03-12 12:01:13 +01:00
svemagie
4e353285b6 Revert "fix: normalize category/tags arrays in isListed and excludeWhereNotes to prevent .map errors"
This reverts commit 841f2650c6.
2026-03-12 12:01:13 +01:00
svemagie
841f2650c6 fix: normalize category/tags arrays in isListed and excludeWhereNotes to prevent .map errors 2026-03-12 11:36:46 +01:00
svemagie
f04c48e3cb fix: hide private and where/Loc notes from public overviews and collections 2026-03-12 11:28:23 +01:00
svemagie
dfb518facd fix: update webmentions feed URL to production domain blog.giersig.eu 2026-03-11 11:29:04 +01:00
svemagie
f31243781f Implement AI frontmatter defaults and metadata outputs 2026-03-09 13:12:13 +01:00
svemagie
182d0fd26e feat(listings): hide unlisted posts from blog and notes 2026-03-08 16:52:54 +01:00
svemagie
13b223ce2a feat(home): hide unlisted posts from recent lists 2026-03-08 16:31:53 +01:00
svemagie
44eca63f10 Merge remote-tracking branch 'theme-upstream/main' 2026-03-07 23:22:52 +01:00
Ricardo
6ff40c8317 perf: address PageSpeed Insights issues (CLS, contrast, touch targets, JS minification)
- Reserve sidebar min-height on desktop to prevent CLS from Alpine.js hydration
- Defer lite-yt-embed.css with media="print" onload pattern
- Add terser JS minification in eleventy.after build hook
- Increase touch target sizing for category pills, facepile avatars, nav items
- Fix text-surface-400 contrast ratio (3.05:1 → 6.23:1) across 20 instances

Confab-Link: http://localhost:8080/sessions/edb1b7b0-da66-4486-bd9c-d1cfa7553b88
2026-03-07 20:13:45 +01:00
Ricardo
e236b4bf65 a11y: comprehensive WCAG 2.1 Level AA accessibility audit
- Add skip-to-main-content link and main content ID target
- Add prefers-reduced-motion media queries for all animations
- Enhance visible focus indicators (2px offset, high-contrast ring)
- Replace ~160 text-surface-500 instances with text-surface-600/dark:text-surface-400
  for 4.5:1+ contrast ratio compliance
- Add aria-hidden="true" to ~30+ decorative SVG icons across sidebars/widgets
- Convert facepile containers from div to semantic ul/li with role="list"
- Add aria-label to icon-only buttons (share, sort controls)
- Add sr-only labels to form inputs (webmention, search)
- Add aria-live="polite" to dynamically loaded webmentions
- Add aria-label with relative+absolute date to time-difference component
- Add keyboard handlers (Enter/Space) to custom interactive elements
- Add aria-label to nav landmarks (table of contents)
- Fix modal focus trap and dialog accessibility
- Fix lightbox keyboard navigation and screen reader announcements

Confab-Link: http://localhost:8080/sessions/edb1b7b0-da66-4486-bd9c-d1cfa7553b88
2026-03-07 18:58:08 +01:00