diff --git a/docs/design-guide.md b/docs/design-guide.md index c9484ba..f0747d3 100644 --- a/docs/design-guide.md +++ b/docs/design-guide.md @@ -32,6 +32,16 @@ Read this **before** touching theme, layout, typography, color, or SEO markup. P - **Mobile-first**: design 390px, then desktop 1280px. - **Astro Image** for automatic optimization/WebP (Core Web Vitals win). +- **Image format policy (measured 2026-10-16 — do not guess this one):** + + | Asset | Served as | Why | + |---|---|---| + | `og:image` / `twitter:image` | **PNG** (1200×630) | Social scrapers (WhatsApp / LinkedIn / FB / Telegram) do not reliably render WebP share cards. Compatibility beats bytes here. | + | Article banner in-page | **WebP q78** + PNG fallback via `` | 6.02 MB → 2.52 MB across 70 banners (42%); JPEG would be *larger* (q85 = 129% of PNG) because the art is flat terminal-style with sharp text edges. | + | List/related thumbnails | **WebP q72 at 400w + 800w** (`/og/thumb/-400.webp`) | Rows display at ~398px; they used to fetch the 1200×630 PNG (~45 KB). 400w is 5.8 KB, 800w is 16.1 KB. The homepage went from ~450 KB of thumbnails to ~62 KB. | + | Real photos (author, hardware) | **WebP q82** + JPEG fallback | Continuous-tone images are the opposite case from the flat art above. | + + Regenerate all derivatives after adding a post: `uv run --with pillow python scripts/optimize-images.py`. - **Declare `og:image:width/height` explicitly** — mis-declared dimensions get preview cards rejected by WhatsApp/Telegram/LinkedIn. - **Favicon**: reference all sizes (`.ico` + 32×32 + 192×192 PNG + apple-touch 180×180). "Favicon not showing" is usually browser cache, not a missing asset — `curl` the live files before "fixing". diff --git a/docs/ops-runbook.md b/docs/ops-runbook.md index 074cdcb..c82f6fa 100644 --- a/docs/ops-runbook.md +++ b/docs/ops-runbook.md @@ -21,6 +21,11 @@ Markdown (src/content/posts) → Astro build (dist/) → Gitea Actions → act_r 1. Create `src/content/posts/.md` with frontmatter (see `content-guide.md` §6). 2. Push to `main`. 3. CI builds `dist/` and deploys to the nginx container automatically. + - **Before pushing a post that adds art**, generate its in-page derivatives: + `uv run --with pillow python scripts/optimize-images.py ` + (banner `.webp` + `/og/thumb/-{400,800}.webp`). The PNGs from + `banner-gen` / `og-gen` remain the `og:image` source of truth — see + `design-guide.md` §3 for the format policy and measured sizes. 4. Verify live: `curl -I https://blog.hoelee.com/` → expect 200. --- diff --git a/docs/project-state.md b/docs/project-state.md index 6ab466d..d6081c2 100644 --- a/docs/project-state.md +++ b/docs/project-state.md @@ -28,6 +28,12 @@ Living list of what's done and what's next for blog.hoelee.com. Work through the - ✅ Knowledge guides in `docs/` + README index + `hoelee-blog` skill - ✅ Both repos public (Gitea + GitHub) with title/description/homepage/topics + `v1.0.0` release - ✅ **Live re-audit of the Sept research recorded (§2 Phase E + §4), 2026-09-29** — 53 posts × 2 languages, 127 sitemap URLs, 133/133 internal links 200 +- ✅ **B1 image batch, 2026-10-16 (first batch from the external design review)** — measured with a real browser before/after, not by eye: + - **Article banner** → `` with WebP q78 + PNG fallback: 70 banners 6.02 MB → **2.52 MB (42%)**. The PNG stays for `og:image` (social scrapers do not reliably render WebP cards). JPEG was measured *worse* than PNG on this art (q85 = 129%). + - **Related-post cards** on a post page → **text-only** (category badge + title + date + read time). They used to load four 1200×630 PNGs (183 KB) to display 526×276 where the art's code is illegible anyway. + - **List thumbnails** (home + archive) → `/og/thumb/-{400,800}.webp` with `srcset`/`sizes` (400w 5.8 KB, 800w 16.1 KB). Homepage thumbnails **~450 KB → 62 KB**; archive (71 rows) **~3.2 MB → 436 KB**. They were fetching full 1200×630 PNGs for a 398px slot. + - **Desktop banner height** capped (720px, centred): 67% → **45%** of a 900px viewport, art intact rather than cropped. + - Measured post page: images **277 KB → 38 KB**, height **5,758 → 5,065px** desktop / 9,423 → 8,807px mobile. Script: `scripts/optimize-images.py`; format policy: `design-guide.md` §3; runbook step in `ops-runbook.md`. --- diff --git a/public/banners/adding-a-domain-to-someone-elses-mail-server.webp b/public/banners/adding-a-domain-to-someone-elses-mail-server.webp new file mode 100644 index 0000000..ef1bbe4 Binary files /dev/null and b/public/banners/adding-a-domain-to-someone-elses-mail-server.webp differ diff --git a/public/banners/adding-english-mode-to-a-chinese-only-web-app.webp b/public/banners/adding-english-mode-to-a-chinese-only-web-app.webp new file mode 100644 index 0000000..14dd7c0 Binary files /dev/null and b/public/banners/adding-english-mode-to-a-chinese-only-web-app.webp differ diff --git a/public/banners/ai-furniture-compositing-with-flux-kontext.webp b/public/banners/ai-furniture-compositing-with-flux-kontext.webp new file mode 100644 index 0000000..8344493 Binary files /dev/null and b/public/banners/ai-furniture-compositing-with-flux-kontext.webp differ diff --git a/public/banners/authentik-css-greater-than-bug.webp b/public/banners/authentik-css-greater-than-bug.webp new file mode 100644 index 0000000..ad1784d Binary files /dev/null and b/public/banners/authentik-css-greater-than-bug.webp differ diff --git a/public/banners/authentik-forward-auth-gate-wasnt-live.webp b/public/banners/authentik-forward-auth-gate-wasnt-live.webp new file mode 100644 index 0000000..b63fece Binary files /dev/null and b/public/banners/authentik-forward-auth-gate-wasnt-live.webp differ diff --git a/public/banners/authentik-major-upgrade-gotchas.webp b/public/banners/authentik-major-upgrade-gotchas.webp new file mode 100644 index 0000000..308a6d0 Binary files /dev/null and b/public/banners/authentik-major-upgrade-gotchas.webp differ diff --git a/public/banners/automating-cyberpanel-without-the-ui.webp b/public/banners/automating-cyberpanel-without-the-ui.webp new file mode 100644 index 0000000..2bfd0ee Binary files /dev/null and b/public/banners/automating-cyberpanel-without-the-ui.webp differ diff --git a/public/banners/best-ai-video-generators-2026.webp b/public/banners/best-ai-video-generators-2026.webp new file mode 100644 index 0000000..3d58ec1 Binary files /dev/null and b/public/banners/best-ai-video-generators-2026.webp differ diff --git a/public/banners/calibrating-intel-quick-sync-hevc-quality.webp b/public/banners/calibrating-intel-quick-sync-hevc-quality.webp new file mode 100644 index 0000000..11a760e Binary files /dev/null and b/public/banners/calibrating-intel-quick-sync-hevc-quality.webp differ diff --git a/public/banners/chainlink-vrf-v2-lottery-contract.webp b/public/banners/chainlink-vrf-v2-lottery-contract.webp new file mode 100644 index 0000000..f46e57d Binary files /dev/null and b/public/banners/chainlink-vrf-v2-lottery-contract.webp differ diff --git a/public/banners/docker-reported-exit-0-the-oom-killer-disagreed.webp b/public/banners/docker-reported-exit-0-the-oom-killer-disagreed.webp new file mode 100644 index 0000000..9483c92 Binary files /dev/null and b/public/banners/docker-reported-exit-0-the-oom-killer-disagreed.webp differ diff --git a/public/banners/does-hevc-actually-shrink-your-files.webp b/public/banners/does-hevc-actually-shrink-your-files.webp new file mode 100644 index 0000000..f9ae167 Binary files /dev/null and b/public/banners/does-hevc-actually-shrink-your-files.webp differ diff --git a/public/banners/embedding-fonts-so-a-pdf-looks-the-same-everywhere.webp b/public/banners/embedding-fonts-so-a-pdf-looks-the-same-everywhere.webp new file mode 100644 index 0000000..8fa6c0c Binary files /dev/null and b/public/banners/embedding-fonts-so-a-pdf-looks-the-same-everywhere.webp differ diff --git a/public/banners/every-player-stuttered-the-file-was-fine.webp b/public/banners/every-player-stuttered-the-file-was-fine.webp new file mode 100644 index 0000000..72f49ae Binary files /dev/null and b/public/banners/every-player-stuttered-the-file-was-fine.webp differ diff --git a/public/banners/five-step-ai-roadmap-what-i-actually-run.webp b/public/banners/five-step-ai-roadmap-what-i-actually-run.webp new file mode 100644 index 0000000..6e377ad Binary files /dev/null and b/public/banners/five-step-ai-roadmap-what-i-actually-run.webp differ diff --git a/public/banners/fully-on-chain-svg-nfts.webp b/public/banners/fully-on-chain-svg-nfts.webp new file mode 100644 index 0000000..c1e6639 Binary files /dev/null and b/public/banners/fully-on-chain-svg-nfts.webp differ diff --git a/public/banners/hardening-a-tor-onion-service.webp b/public/banners/hardening-a-tor-onion-service.webp new file mode 100644 index 0000000..02fccb3 Binary files /dev/null and b/public/banners/hardening-a-tor-onion-service.webp differ diff --git a/public/banners/hello-world.webp b/public/banners/hello-world.webp new file mode 100644 index 0000000..bb237f9 Binary files /dev/null and b/public/banners/hello-world.webp differ diff --git a/public/banners/how-i-built-the-digikedai-telegram-bot.webp b/public/banners/how-i-built-the-digikedai-telegram-bot.webp new file mode 100644 index 0000000..a12a072 Binary files /dev/null and b/public/banners/how-i-built-the-digikedai-telegram-bot.webp differ diff --git a/public/banners/how-i-host-this-blog.webp b/public/banners/how-i-host-this-blog.webp new file mode 100644 index 0000000..463b0e8 Binary files /dev/null and b/public/banners/how-i-host-this-blog.webp differ diff --git a/public/banners/how-i-made-my-own-songs-with-suno-ai.webp b/public/banners/how-i-made-my-own-songs-with-suno-ai.webp new file mode 100644 index 0000000..4363133 Binary files /dev/null and b/public/banners/how-i-made-my-own-songs-with-suno-ai.webp differ diff --git a/public/banners/how-i-vetted-20-vps-providers-with-parallel-subagents.webp b/public/banners/how-i-vetted-20-vps-providers-with-parallel-subagents.webp new file mode 100644 index 0000000..dba511f Binary files /dev/null and b/public/banners/how-i-vetted-20-vps-providers-with-parallel-subagents.webp differ diff --git a/public/banners/how-to-verify-a-hosting-provider-before-you-buy.webp b/public/banners/how-to-verify-a-hosting-provider-before-you-buy.webp new file mode 100644 index 0000000..113b148 Binary files /dev/null and b/public/banners/how-to-verify-a-hosting-provider-before-you-buy.webp differ diff --git a/public/banners/i-let-an-agent-manage-my-shopee-vouchers.webp b/public/banners/i-let-an-agent-manage-my-shopee-vouchers.webp new file mode 100644 index 0000000..6244cad Binary files /dev/null and b/public/banners/i-let-an-agent-manage-my-shopee-vouchers.webp differ diff --git a/public/banners/it-reported-success-nothing-had-changed.webp b/public/banners/it-reported-success-nothing-had-changed.webp new file mode 100644 index 0000000..07c58bb Binary files /dev/null and b/public/banners/it-reported-success-nothing-had-changed.webp differ diff --git a/public/banners/jpa-version-field-lost-update.webp b/public/banners/jpa-version-field-lost-update.webp new file mode 100644 index 0000000..287c140 Binary files /dev/null and b/public/banners/jpa-version-field-lost-update.webp differ diff --git a/public/banners/loop-engineering-without-a-coding-agent.webp b/public/banners/loop-engineering-without-a-coding-agent.webp new file mode 100644 index 0000000..bebc8a2 Binary files /dev/null and b/public/banners/loop-engineering-without-a-coding-agent.webp differ diff --git a/public/banners/migrating-codeigniter-iis-to-openlitespeed.webp b/public/banners/migrating-codeigniter-iis-to-openlitespeed.webp new file mode 100644 index 0000000..dadaa8d Binary files /dev/null and b/public/banners/migrating-codeigniter-iis-to-openlitespeed.webp differ diff --git a/public/banners/my-backup-was-using-someone-elses-api-quota.webp b/public/banners/my-backup-was-using-someone-elses-api-quota.webp new file mode 100644 index 0000000..17abeae Binary files /dev/null and b/public/banners/my-backup-was-using-someone-elses-api-quota.webp differ diff --git a/public/banners/n8n-v1-to-v2-upgrade-gotchas.webp b/public/banners/n8n-v1-to-v2-upgrade-gotchas.webp new file mode 100644 index 0000000..b552758 Binary files /dev/null and b/public/banners/n8n-v1-to-v2-upgrade-gotchas.webp differ diff --git a/public/banners/nocodb-sso-is-a-licensed-feature.webp b/public/banners/nocodb-sso-is-a-licensed-feature.webp new file mode 100644 index 0000000..0b07262 Binary files /dev/null and b/public/banners/nocodb-sso-is-a-licensed-feature.webp differ diff --git a/public/banners/one-hostname-public-tracker-sso-dashboard.webp b/public/banners/one-hostname-public-tracker-sso-dashboard.webp new file mode 100644 index 0000000..4f5d2d9 Binary files /dev/null and b/public/banners/one-hostname-public-tracker-sso-dashboard.webp differ diff --git a/public/banners/one-prometheus-for-unraid-synology-and-a-vps.webp b/public/banners/one-prometheus-for-unraid-synology-and-a-vps.webp new file mode 100644 index 0000000..94bdf31 Binary files /dev/null and b/public/banners/one-prometheus-for-unraid-synology-and-a-vps.webp differ diff --git a/public/banners/passbolt-hang-three-failure-modes.webp b/public/banners/passbolt-hang-three-failure-modes.webp new file mode 100644 index 0000000..ab20f94 Binary files /dev/null and b/public/banners/passbolt-hang-three-failure-modes.webp differ diff --git a/public/banners/patching-workbench-26-for-mariadb.webp b/public/banners/patching-workbench-26-for-mariadb.webp new file mode 100644 index 0000000..996037a Binary files /dev/null and b/public/banners/patching-workbench-26-for-mariadb.webp differ diff --git a/public/banners/putting-a-front-door-on-an-app-you-cant-modify.webp b/public/banners/putting-a-front-door-on-an-app-you-cant-modify.webp new file mode 100644 index 0000000..2329c86 Binary files /dev/null and b/public/banners/putting-a-front-door-on-an-app-you-cant-modify.webp differ diff --git a/public/banners/read-only-nocodb-dashboard-for-a-remote-database.webp b/public/banners/read-only-nocodb-dashboard-for-a-remote-database.webp new file mode 100644 index 0000000..06a4678 Binary files /dev/null and b/public/banners/read-only-nocodb-dashboard-for-a-remote-database.webp differ diff --git a/public/banners/reading-a-containers-own-api-docs.webp b/public/banners/reading-a-containers-own-api-docs.webp new file mode 100644 index 0000000..c7aca6a Binary files /dev/null and b/public/banners/reading-a-containers-own-api-docs.webp differ diff --git a/public/banners/replacing-rdpguard-with-ipban.webp b/public/banners/replacing-rdpguard-with-ipban.webp new file mode 100644 index 0000000..331d210 Binary files /dev/null and b/public/banners/replacing-rdpguard-with-ipban.webp differ diff --git a/public/banners/running-production-infrastructure-solo.webp b/public/banners/running-production-infrastructure-solo.webp new file mode 100644 index 0000000..2a62638 Binary files /dev/null and b/public/banners/running-production-infrastructure-solo.webp differ diff --git a/public/banners/running-tts-as-a-service-with-token-sidecars.webp b/public/banners/running-tts-as-a-service-with-token-sidecars.webp new file mode 100644 index 0000000..f182df6 Binary files /dev/null and b/public/banners/running-tts-as-a-service-with-token-sidecars.webp differ diff --git a/public/banners/self-healing-digital-goods-entitlements.webp b/public/banners/self-healing-digital-goods-entitlements.webp new file mode 100644 index 0000000..94e50f2 Binary files /dev/null and b/public/banners/self-healing-digital-goods-entitlements.webp differ diff --git a/public/banners/self-hosted-speech-to-text-api.webp b/public/banners/self-hosted-speech-to-text-api.webp new file mode 100644 index 0000000..ca91719 Binary files /dev/null and b/public/banners/self-hosted-speech-to-text-api.webp differ diff --git a/public/banners/self-hosting-mem0-memory-stack.webp b/public/banners/self-hosting-mem0-memory-stack.webp new file mode 100644 index 0000000..30d81cf Binary files /dev/null and b/public/banners/self-hosting-mem0-memory-stack.webp differ diff --git a/public/banners/shipping-an-ai-photo-editor-as-a-wordpress-plugin.webp b/public/banners/shipping-an-ai-photo-editor-as-a-wordpress-plugin.webp new file mode 100644 index 0000000..086d7ed Binary files /dev/null and b/public/banners/shipping-an-ai-photo-editor-as-a-wordpress-plugin.webp differ diff --git a/public/banners/smartctl-exit-code-32-skips-the-disks-that-matter.webp b/public/banners/smartctl-exit-code-32-skips-the-disks-that-matter.webp new file mode 100644 index 0000000..81307d9 Binary files /dev/null and b/public/banners/smartctl-exit-code-32-skips-the-disks-that-matter.webp differ diff --git a/public/banners/space-reclamation-wasnt-stuck-it-was-waiting.webp b/public/banners/space-reclamation-wasnt-stuck-it-was-waiting.webp new file mode 100644 index 0000000..09430f7 Binary files /dev/null and b/public/banners/space-reclamation-wasnt-stuck-it-was-waiting.webp differ diff --git a/public/banners/stop-writing-agent-prompts-for-deterministic-work.webp b/public/banners/stop-writing-agent-prompts-for-deterministic-work.webp new file mode 100644 index 0000000..3c1b4f3 Binary files /dev/null and b/public/banners/stop-writing-agent-prompts-for-deterministic-work.webp differ diff --git a/public/banners/syncing-a-self-improving-ai-agent-across-machines.webp b/public/banners/syncing-a-self-improving-ai-agent-across-machines.webp new file mode 100644 index 0000000..c5c9c07 Binary files /dev/null and b/public/banners/syncing-a-self-improving-ai-agent-across-machines.webp differ diff --git a/public/banners/synology-api-401-with-the-correct-password.webp b/public/banners/synology-api-401-with-the-correct-password.webp new file mode 100644 index 0000000..9ee3689 Binary files /dev/null and b/public/banners/synology-api-401-with-the-correct-password.webp differ diff --git a/public/banners/synology-spreadsheet-api-is-a-container.webp b/public/banners/synology-spreadsheet-api-is-a-container.webp new file mode 100644 index 0000000..cfe2b25 Binary files /dev/null and b/public/banners/synology-spreadsheet-api-is-a-container.webp differ diff --git a/public/banners/that-dying-ssd-was-just-a-bad-sata-cable.webp b/public/banners/that-dying-ssd-was-just-a-bad-sata-cable.webp new file mode 100644 index 0000000..15fc5ce Binary files /dev/null and b/public/banners/that-dying-ssd-was-just-a-bad-sata-cable.webp differ diff --git a/public/banners/the-cause-was-trim-not-the-ssds.webp b/public/banners/the-cause-was-trim-not-the-ssds.webp new file mode 100644 index 0000000..b765177 Binary files /dev/null and b/public/banners/the-cause-was-trim-not-the-ssds.webp differ diff --git a/public/banners/the-nocodb-attachment-that-wouldnt-update.webp b/public/banners/the-nocodb-attachment-that-wouldnt-update.webp new file mode 100644 index 0000000..d7767ec Binary files /dev/null and b/public/banners/the-nocodb-attachment-that-wouldnt-update.webp differ diff --git a/public/banners/the-upstream-was-deleted-then-came-back-rewritten.webp b/public/banners/the-upstream-was-deleted-then-came-back-rewritten.webp new file mode 100644 index 0000000..02cee30 Binary files /dev/null and b/public/banners/the-upstream-was-deleted-then-came-back-rewritten.webp differ diff --git a/public/banners/two-ways-a-javascript-file-stops-working-and-looks-fine.webp b/public/banners/two-ways-a-javascript-file-stops-working-and-looks-fine.webp new file mode 100644 index 0000000..f601ec1 Binary files /dev/null and b/public/banners/two-ways-a-javascript-file-stops-working-and-looks-fine.webp differ diff --git a/public/banners/unraid-stop-array-hangs-on-swapfile.webp b/public/banners/unraid-stop-array-hangs-on-swapfile.webp new file mode 100644 index 0000000..1fdbf59 Binary files /dev/null and b/public/banners/unraid-stop-array-hangs-on-swapfile.webp differ diff --git a/public/banners/upgrading-codeigniter-46-to-47.webp b/public/banners/upgrading-codeigniter-46-to-47.webp new file mode 100644 index 0000000..b268928 Binary files /dev/null and b/public/banners/upgrading-codeigniter-46-to-47.webp differ diff --git a/public/banners/using-chinese-llm-apis-from-malaysia.webp b/public/banners/using-chinese-llm-apis-from-malaysia.webp new file mode 100644 index 0000000..d18b76d Binary files /dev/null and b/public/banners/using-chinese-llm-apis-from-malaysia.webp differ diff --git a/public/banners/verifying-a-pdf-report-page-by-page.webp b/public/banners/verifying-a-pdf-report-page-by-page.webp new file mode 100644 index 0000000..a85c267 Binary files /dev/null and b/public/banners/verifying-a-pdf-report-page-by-page.webp differ diff --git a/public/banners/vetting-an-open-source-dependency-before-you-bet-on-it.webp b/public/banners/vetting-an-open-source-dependency-before-you-bet-on-it.webp new file mode 100644 index 0000000..e342470 Binary files /dev/null and b/public/banners/vetting-an-open-source-dependency-before-you-bet-on-it.webp differ diff --git a/public/banners/when-smart-says-healthy-but-your-raid-is-corrupting-data.webp b/public/banners/when-smart-says-healthy-but-your-raid-is-corrupting-data.webp new file mode 100644 index 0000000..ece1235 Binary files /dev/null and b/public/banners/when-smart-says-healthy-but-your-raid-is-corrupting-data.webp differ diff --git a/public/banners/why-chrome-forgets-its-tabs-in-a-container.webp b/public/banners/why-chrome-forgets-its-tabs-in-a-container.webp new file mode 100644 index 0000000..afda48d Binary files /dev/null and b/public/banners/why-chrome-forgets-its-tabs-in-a-container.webp differ diff --git a/public/banners/why-i-still-bought-a-local-gpu.webp b/public/banners/why-i-still-bought-a-local-gpu.webp new file mode 100644 index 0000000..2da4a47 Binary files /dev/null and b/public/banners/why-i-still-bought-a-local-gpu.webp differ diff --git a/public/banners/why-my-on-chain-nft-art-changed-on-windows.webp b/public/banners/why-my-on-chain-nft-art-changed-on-windows.webp new file mode 100644 index 0000000..d832953 Binary files /dev/null and b/public/banners/why-my-on-chain-nft-art-changed-on-windows.webp differ diff --git a/public/banners/why-telegram-bot-notifications-die.webp b/public/banners/why-telegram-bot-notifications-die.webp new file mode 100644 index 0000000..d563eea Binary files /dev/null and b/public/banners/why-telegram-bot-notifications-die.webp differ diff --git a/public/banners/why-your-cdp-clicks-silently-fail.webp b/public/banners/why-your-cdp-clicks-silently-fail.webp new file mode 100644 index 0000000..b8b73b5 Binary files /dev/null and b/public/banners/why-your-cdp-clicks-silently-fail.webp differ diff --git a/public/banners/why-your-grafana-dashboard-shows-no-data.webp b/public/banners/why-your-grafana-dashboard-shows-no-data.webp new file mode 100644 index 0000000..107662f Binary files /dev/null and b/public/banners/why-your-grafana-dashboard-shows-no-data.webp differ diff --git a/public/banners/why-your-headless-browser-cant-scrape-everything.webp b/public/banners/why-your-headless-browser-cant-scrape-everything.webp new file mode 100644 index 0000000..48351de Binary files /dev/null and b/public/banners/why-your-headless-browser-cant-scrape-everything.webp differ diff --git a/public/banners/your-disk-full-alert-is-lying.webp b/public/banners/your-disk-full-alert-is-lying.webp new file mode 100644 index 0000000..a5a2949 Binary files /dev/null and b/public/banners/your-disk-full-alert-is-lying.webp differ diff --git a/public/og/thumb/adding-a-domain-to-someone-elses-mail-server-400.webp b/public/og/thumb/adding-a-domain-to-someone-elses-mail-server-400.webp new file mode 100644 index 0000000..9686c18 Binary files /dev/null and b/public/og/thumb/adding-a-domain-to-someone-elses-mail-server-400.webp differ diff --git a/public/og/thumb/adding-a-domain-to-someone-elses-mail-server-800.webp b/public/og/thumb/adding-a-domain-to-someone-elses-mail-server-800.webp new file mode 100644 index 0000000..52fd698 Binary files /dev/null and b/public/og/thumb/adding-a-domain-to-someone-elses-mail-server-800.webp differ diff --git a/public/og/thumb/adding-english-mode-to-a-chinese-only-web-app-400.webp b/public/og/thumb/adding-english-mode-to-a-chinese-only-web-app-400.webp new file mode 100644 index 0000000..d7cdf62 Binary files /dev/null and b/public/og/thumb/adding-english-mode-to-a-chinese-only-web-app-400.webp differ diff --git a/public/og/thumb/adding-english-mode-to-a-chinese-only-web-app-800.webp b/public/og/thumb/adding-english-mode-to-a-chinese-only-web-app-800.webp new file mode 100644 index 0000000..5d9a49c Binary files /dev/null and b/public/og/thumb/adding-english-mode-to-a-chinese-only-web-app-800.webp differ diff --git a/public/og/thumb/ai-furniture-compositing-with-flux-kontext-400.webp b/public/og/thumb/ai-furniture-compositing-with-flux-kontext-400.webp new file mode 100644 index 0000000..5c4321b Binary files /dev/null and b/public/og/thumb/ai-furniture-compositing-with-flux-kontext-400.webp differ diff --git a/public/og/thumb/ai-furniture-compositing-with-flux-kontext-800.webp b/public/og/thumb/ai-furniture-compositing-with-flux-kontext-800.webp new file mode 100644 index 0000000..1466de7 Binary files /dev/null and b/public/og/thumb/ai-furniture-compositing-with-flux-kontext-800.webp differ diff --git a/public/og/thumb/authentik-css-greater-than-bug-400.webp b/public/og/thumb/authentik-css-greater-than-bug-400.webp new file mode 100644 index 0000000..460a9c6 Binary files /dev/null and b/public/og/thumb/authentik-css-greater-than-bug-400.webp differ diff --git a/public/og/thumb/authentik-css-greater-than-bug-800.webp b/public/og/thumb/authentik-css-greater-than-bug-800.webp new file mode 100644 index 0000000..6af3547 Binary files /dev/null and b/public/og/thumb/authentik-css-greater-than-bug-800.webp differ diff --git a/public/og/thumb/authentik-forward-auth-gate-wasnt-live-400.webp b/public/og/thumb/authentik-forward-auth-gate-wasnt-live-400.webp new file mode 100644 index 0000000..65cb6d9 Binary files /dev/null and b/public/og/thumb/authentik-forward-auth-gate-wasnt-live-400.webp differ diff --git a/public/og/thumb/authentik-forward-auth-gate-wasnt-live-800.webp b/public/og/thumb/authentik-forward-auth-gate-wasnt-live-800.webp new file mode 100644 index 0000000..57d3d01 Binary files /dev/null and b/public/og/thumb/authentik-forward-auth-gate-wasnt-live-800.webp differ diff --git a/public/og/thumb/authentik-major-upgrade-gotchas-400.webp b/public/og/thumb/authentik-major-upgrade-gotchas-400.webp new file mode 100644 index 0000000..e3710b2 Binary files /dev/null and b/public/og/thumb/authentik-major-upgrade-gotchas-400.webp differ diff --git a/public/og/thumb/authentik-major-upgrade-gotchas-800.webp b/public/og/thumb/authentik-major-upgrade-gotchas-800.webp new file mode 100644 index 0000000..5176d67 Binary files /dev/null and b/public/og/thumb/authentik-major-upgrade-gotchas-800.webp differ diff --git a/public/og/thumb/automating-cyberpanel-without-the-ui-400.webp b/public/og/thumb/automating-cyberpanel-without-the-ui-400.webp new file mode 100644 index 0000000..5ee0b4e Binary files /dev/null and b/public/og/thumb/automating-cyberpanel-without-the-ui-400.webp differ diff --git a/public/og/thumb/automating-cyberpanel-without-the-ui-800.webp b/public/og/thumb/automating-cyberpanel-without-the-ui-800.webp new file mode 100644 index 0000000..26ee93d Binary files /dev/null and b/public/og/thumb/automating-cyberpanel-without-the-ui-800.webp differ diff --git a/public/og/thumb/best-ai-video-generators-2026-400.webp b/public/og/thumb/best-ai-video-generators-2026-400.webp new file mode 100644 index 0000000..9c42b75 Binary files /dev/null and b/public/og/thumb/best-ai-video-generators-2026-400.webp differ diff --git a/public/og/thumb/best-ai-video-generators-2026-800.webp b/public/og/thumb/best-ai-video-generators-2026-800.webp new file mode 100644 index 0000000..50aea71 Binary files /dev/null and b/public/og/thumb/best-ai-video-generators-2026-800.webp differ diff --git a/public/og/thumb/calibrating-intel-quick-sync-hevc-quality-400.webp b/public/og/thumb/calibrating-intel-quick-sync-hevc-quality-400.webp new file mode 100644 index 0000000..8e614ee Binary files /dev/null and b/public/og/thumb/calibrating-intel-quick-sync-hevc-quality-400.webp differ diff --git a/public/og/thumb/calibrating-intel-quick-sync-hevc-quality-800.webp b/public/og/thumb/calibrating-intel-quick-sync-hevc-quality-800.webp new file mode 100644 index 0000000..e5319c5 Binary files /dev/null and b/public/og/thumb/calibrating-intel-quick-sync-hevc-quality-800.webp differ diff --git a/public/og/thumb/chainlink-vrf-v2-lottery-contract-400.webp b/public/og/thumb/chainlink-vrf-v2-lottery-contract-400.webp new file mode 100644 index 0000000..8a72a99 Binary files /dev/null and b/public/og/thumb/chainlink-vrf-v2-lottery-contract-400.webp differ diff --git a/public/og/thumb/chainlink-vrf-v2-lottery-contract-800.webp b/public/og/thumb/chainlink-vrf-v2-lottery-contract-800.webp new file mode 100644 index 0000000..802235c Binary files /dev/null and b/public/og/thumb/chainlink-vrf-v2-lottery-contract-800.webp differ diff --git a/public/og/thumb/docker-reported-exit-0-the-oom-killer-disagreed-400.webp b/public/og/thumb/docker-reported-exit-0-the-oom-killer-disagreed-400.webp new file mode 100644 index 0000000..8a5b636 Binary files /dev/null and b/public/og/thumb/docker-reported-exit-0-the-oom-killer-disagreed-400.webp differ diff --git a/public/og/thumb/docker-reported-exit-0-the-oom-killer-disagreed-800.webp b/public/og/thumb/docker-reported-exit-0-the-oom-killer-disagreed-800.webp new file mode 100644 index 0000000..fd89650 Binary files /dev/null and b/public/og/thumb/docker-reported-exit-0-the-oom-killer-disagreed-800.webp differ diff --git a/public/og/thumb/does-hevc-actually-shrink-your-files-400.webp b/public/og/thumb/does-hevc-actually-shrink-your-files-400.webp new file mode 100644 index 0000000..1bf037c Binary files /dev/null and b/public/og/thumb/does-hevc-actually-shrink-your-files-400.webp differ diff --git a/public/og/thumb/does-hevc-actually-shrink-your-files-800.webp b/public/og/thumb/does-hevc-actually-shrink-your-files-800.webp new file mode 100644 index 0000000..0fab0e0 Binary files /dev/null and b/public/og/thumb/does-hevc-actually-shrink-your-files-800.webp differ diff --git a/public/og/thumb/embedding-fonts-so-a-pdf-looks-the-same-everywhere-400.webp b/public/og/thumb/embedding-fonts-so-a-pdf-looks-the-same-everywhere-400.webp new file mode 100644 index 0000000..57af882 Binary files /dev/null and b/public/og/thumb/embedding-fonts-so-a-pdf-looks-the-same-everywhere-400.webp differ diff --git a/public/og/thumb/embedding-fonts-so-a-pdf-looks-the-same-everywhere-800.webp b/public/og/thumb/embedding-fonts-so-a-pdf-looks-the-same-everywhere-800.webp new file mode 100644 index 0000000..ea4a714 Binary files /dev/null and b/public/og/thumb/embedding-fonts-so-a-pdf-looks-the-same-everywhere-800.webp differ diff --git a/public/og/thumb/every-player-stuttered-the-file-was-fine-400.webp b/public/og/thumb/every-player-stuttered-the-file-was-fine-400.webp new file mode 100644 index 0000000..b9e94bc Binary files /dev/null and b/public/og/thumb/every-player-stuttered-the-file-was-fine-400.webp differ diff --git a/public/og/thumb/every-player-stuttered-the-file-was-fine-800.webp b/public/og/thumb/every-player-stuttered-the-file-was-fine-800.webp new file mode 100644 index 0000000..a70484e Binary files /dev/null and b/public/og/thumb/every-player-stuttered-the-file-was-fine-800.webp differ diff --git a/public/og/thumb/five-step-ai-roadmap-what-i-actually-run-400.webp b/public/og/thumb/five-step-ai-roadmap-what-i-actually-run-400.webp new file mode 100644 index 0000000..c40d458 Binary files /dev/null and b/public/og/thumb/five-step-ai-roadmap-what-i-actually-run-400.webp differ diff --git a/public/og/thumb/five-step-ai-roadmap-what-i-actually-run-800.webp b/public/og/thumb/five-step-ai-roadmap-what-i-actually-run-800.webp new file mode 100644 index 0000000..7004ea1 Binary files /dev/null and b/public/og/thumb/five-step-ai-roadmap-what-i-actually-run-800.webp differ diff --git a/public/og/thumb/fully-on-chain-svg-nfts-400.webp b/public/og/thumb/fully-on-chain-svg-nfts-400.webp new file mode 100644 index 0000000..029612c Binary files /dev/null and b/public/og/thumb/fully-on-chain-svg-nfts-400.webp differ diff --git a/public/og/thumb/fully-on-chain-svg-nfts-800.webp b/public/og/thumb/fully-on-chain-svg-nfts-800.webp new file mode 100644 index 0000000..1abdd88 Binary files /dev/null and b/public/og/thumb/fully-on-chain-svg-nfts-800.webp differ diff --git a/public/og/thumb/hardening-a-tor-onion-service-400.webp b/public/og/thumb/hardening-a-tor-onion-service-400.webp new file mode 100644 index 0000000..f721ce2 Binary files /dev/null and b/public/og/thumb/hardening-a-tor-onion-service-400.webp differ diff --git a/public/og/thumb/hardening-a-tor-onion-service-800.webp b/public/og/thumb/hardening-a-tor-onion-service-800.webp new file mode 100644 index 0000000..009823c Binary files /dev/null and b/public/og/thumb/hardening-a-tor-onion-service-800.webp differ diff --git a/public/og/thumb/hello-world-400.webp b/public/og/thumb/hello-world-400.webp new file mode 100644 index 0000000..cc59844 Binary files /dev/null and b/public/og/thumb/hello-world-400.webp differ diff --git a/public/og/thumb/hello-world-800.webp b/public/og/thumb/hello-world-800.webp new file mode 100644 index 0000000..7852454 Binary files /dev/null and b/public/og/thumb/hello-world-800.webp differ diff --git a/public/og/thumb/how-i-built-the-digikedai-telegram-bot-400.webp b/public/og/thumb/how-i-built-the-digikedai-telegram-bot-400.webp new file mode 100644 index 0000000..f50f002 Binary files /dev/null and b/public/og/thumb/how-i-built-the-digikedai-telegram-bot-400.webp differ diff --git a/public/og/thumb/how-i-built-the-digikedai-telegram-bot-800.webp b/public/og/thumb/how-i-built-the-digikedai-telegram-bot-800.webp new file mode 100644 index 0000000..08a686e Binary files /dev/null and b/public/og/thumb/how-i-built-the-digikedai-telegram-bot-800.webp differ diff --git a/public/og/thumb/how-i-host-this-blog-400.webp b/public/og/thumb/how-i-host-this-blog-400.webp new file mode 100644 index 0000000..b2d2180 Binary files /dev/null and b/public/og/thumb/how-i-host-this-blog-400.webp differ diff --git a/public/og/thumb/how-i-host-this-blog-800.webp b/public/og/thumb/how-i-host-this-blog-800.webp new file mode 100644 index 0000000..7c2e64d Binary files /dev/null and b/public/og/thumb/how-i-host-this-blog-800.webp differ diff --git a/public/og/thumb/how-i-made-my-own-songs-with-suno-ai-400.webp b/public/og/thumb/how-i-made-my-own-songs-with-suno-ai-400.webp new file mode 100644 index 0000000..1b306e4 Binary files /dev/null and b/public/og/thumb/how-i-made-my-own-songs-with-suno-ai-400.webp differ diff --git a/public/og/thumb/how-i-made-my-own-songs-with-suno-ai-800.webp b/public/og/thumb/how-i-made-my-own-songs-with-suno-ai-800.webp new file mode 100644 index 0000000..0a2e76c Binary files /dev/null and b/public/og/thumb/how-i-made-my-own-songs-with-suno-ai-800.webp differ diff --git a/public/og/thumb/how-i-vetted-20-vps-providers-with-parallel-subagents-400.webp b/public/og/thumb/how-i-vetted-20-vps-providers-with-parallel-subagents-400.webp new file mode 100644 index 0000000..25b0121 Binary files /dev/null and b/public/og/thumb/how-i-vetted-20-vps-providers-with-parallel-subagents-400.webp differ diff --git a/public/og/thumb/how-i-vetted-20-vps-providers-with-parallel-subagents-800.webp b/public/og/thumb/how-i-vetted-20-vps-providers-with-parallel-subagents-800.webp new file mode 100644 index 0000000..f86eb9f Binary files /dev/null and b/public/og/thumb/how-i-vetted-20-vps-providers-with-parallel-subagents-800.webp differ diff --git a/public/og/thumb/how-to-verify-a-hosting-provider-before-you-buy-400.webp b/public/og/thumb/how-to-verify-a-hosting-provider-before-you-buy-400.webp new file mode 100644 index 0000000..98d73b3 Binary files /dev/null and b/public/og/thumb/how-to-verify-a-hosting-provider-before-you-buy-400.webp differ diff --git a/public/og/thumb/how-to-verify-a-hosting-provider-before-you-buy-800.webp b/public/og/thumb/how-to-verify-a-hosting-provider-before-you-buy-800.webp new file mode 100644 index 0000000..7990c4d Binary files /dev/null and b/public/og/thumb/how-to-verify-a-hosting-provider-before-you-buy-800.webp differ diff --git a/public/og/thumb/i-let-an-agent-manage-my-shopee-vouchers-400.webp b/public/og/thumb/i-let-an-agent-manage-my-shopee-vouchers-400.webp new file mode 100644 index 0000000..2e992d9 Binary files /dev/null and b/public/og/thumb/i-let-an-agent-manage-my-shopee-vouchers-400.webp differ diff --git a/public/og/thumb/i-let-an-agent-manage-my-shopee-vouchers-800.webp b/public/og/thumb/i-let-an-agent-manage-my-shopee-vouchers-800.webp new file mode 100644 index 0000000..5894d86 Binary files /dev/null and b/public/og/thumb/i-let-an-agent-manage-my-shopee-vouchers-800.webp differ diff --git a/public/og/thumb/it-reported-success-nothing-had-changed-400.webp b/public/og/thumb/it-reported-success-nothing-had-changed-400.webp new file mode 100644 index 0000000..1b27204 Binary files /dev/null and b/public/og/thumb/it-reported-success-nothing-had-changed-400.webp differ diff --git a/public/og/thumb/it-reported-success-nothing-had-changed-800.webp b/public/og/thumb/it-reported-success-nothing-had-changed-800.webp new file mode 100644 index 0000000..cb02996 Binary files /dev/null and b/public/og/thumb/it-reported-success-nothing-had-changed-800.webp differ diff --git a/public/og/thumb/jpa-version-field-lost-update-400.webp b/public/og/thumb/jpa-version-field-lost-update-400.webp new file mode 100644 index 0000000..adca18a Binary files /dev/null and b/public/og/thumb/jpa-version-field-lost-update-400.webp differ diff --git a/public/og/thumb/jpa-version-field-lost-update-800.webp b/public/og/thumb/jpa-version-field-lost-update-800.webp new file mode 100644 index 0000000..cc01289 Binary files /dev/null and b/public/og/thumb/jpa-version-field-lost-update-800.webp differ diff --git a/public/og/thumb/loop-engineering-without-a-coding-agent-400.webp b/public/og/thumb/loop-engineering-without-a-coding-agent-400.webp new file mode 100644 index 0000000..58c76df Binary files /dev/null and b/public/og/thumb/loop-engineering-without-a-coding-agent-400.webp differ diff --git a/public/og/thumb/loop-engineering-without-a-coding-agent-800.webp b/public/og/thumb/loop-engineering-without-a-coding-agent-800.webp new file mode 100644 index 0000000..8166d3c Binary files /dev/null and b/public/og/thumb/loop-engineering-without-a-coding-agent-800.webp differ diff --git a/public/og/thumb/migrating-codeigniter-iis-to-openlitespeed-400.webp b/public/og/thumb/migrating-codeigniter-iis-to-openlitespeed-400.webp new file mode 100644 index 0000000..09cf92b Binary files /dev/null and b/public/og/thumb/migrating-codeigniter-iis-to-openlitespeed-400.webp differ diff --git a/public/og/thumb/migrating-codeigniter-iis-to-openlitespeed-800.webp b/public/og/thumb/migrating-codeigniter-iis-to-openlitespeed-800.webp new file mode 100644 index 0000000..11d3a2a Binary files /dev/null and b/public/og/thumb/migrating-codeigniter-iis-to-openlitespeed-800.webp differ diff --git a/public/og/thumb/my-backup-was-using-someone-elses-api-quota-400.webp b/public/og/thumb/my-backup-was-using-someone-elses-api-quota-400.webp new file mode 100644 index 0000000..0445994 Binary files /dev/null and b/public/og/thumb/my-backup-was-using-someone-elses-api-quota-400.webp differ diff --git a/public/og/thumb/my-backup-was-using-someone-elses-api-quota-800.webp b/public/og/thumb/my-backup-was-using-someone-elses-api-quota-800.webp new file mode 100644 index 0000000..27e3dcd Binary files /dev/null and b/public/og/thumb/my-backup-was-using-someone-elses-api-quota-800.webp differ diff --git a/public/og/thumb/n8n-v1-to-v2-upgrade-gotchas-400.webp b/public/og/thumb/n8n-v1-to-v2-upgrade-gotchas-400.webp new file mode 100644 index 0000000..997cefb Binary files /dev/null and b/public/og/thumb/n8n-v1-to-v2-upgrade-gotchas-400.webp differ diff --git a/public/og/thumb/n8n-v1-to-v2-upgrade-gotchas-800.webp b/public/og/thumb/n8n-v1-to-v2-upgrade-gotchas-800.webp new file mode 100644 index 0000000..6047a9e Binary files /dev/null and b/public/og/thumb/n8n-v1-to-v2-upgrade-gotchas-800.webp differ diff --git a/public/og/thumb/nocodb-sso-is-a-licensed-feature-400.webp b/public/og/thumb/nocodb-sso-is-a-licensed-feature-400.webp new file mode 100644 index 0000000..86ce082 Binary files /dev/null and b/public/og/thumb/nocodb-sso-is-a-licensed-feature-400.webp differ diff --git a/public/og/thumb/nocodb-sso-is-a-licensed-feature-800.webp b/public/og/thumb/nocodb-sso-is-a-licensed-feature-800.webp new file mode 100644 index 0000000..be3ddca Binary files /dev/null and b/public/og/thumb/nocodb-sso-is-a-licensed-feature-800.webp differ diff --git a/public/og/thumb/one-hostname-public-tracker-sso-dashboard-400.webp b/public/og/thumb/one-hostname-public-tracker-sso-dashboard-400.webp new file mode 100644 index 0000000..bed93a1 Binary files /dev/null and b/public/og/thumb/one-hostname-public-tracker-sso-dashboard-400.webp differ diff --git a/public/og/thumb/one-hostname-public-tracker-sso-dashboard-800.webp b/public/og/thumb/one-hostname-public-tracker-sso-dashboard-800.webp new file mode 100644 index 0000000..b1c6e77 Binary files /dev/null and b/public/og/thumb/one-hostname-public-tracker-sso-dashboard-800.webp differ diff --git a/public/og/thumb/one-prometheus-for-unraid-synology-and-a-vps-400.webp b/public/og/thumb/one-prometheus-for-unraid-synology-and-a-vps-400.webp new file mode 100644 index 0000000..c6207ac Binary files /dev/null and b/public/og/thumb/one-prometheus-for-unraid-synology-and-a-vps-400.webp differ diff --git a/public/og/thumb/one-prometheus-for-unraid-synology-and-a-vps-800.webp b/public/og/thumb/one-prometheus-for-unraid-synology-and-a-vps-800.webp new file mode 100644 index 0000000..19101c4 Binary files /dev/null and b/public/og/thumb/one-prometheus-for-unraid-synology-and-a-vps-800.webp differ diff --git a/public/og/thumb/passbolt-hang-three-failure-modes-400.webp b/public/og/thumb/passbolt-hang-three-failure-modes-400.webp new file mode 100644 index 0000000..5147e94 Binary files /dev/null and b/public/og/thumb/passbolt-hang-three-failure-modes-400.webp differ diff --git a/public/og/thumb/passbolt-hang-three-failure-modes-800.webp b/public/og/thumb/passbolt-hang-three-failure-modes-800.webp new file mode 100644 index 0000000..d4ab710 Binary files /dev/null and b/public/og/thumb/passbolt-hang-three-failure-modes-800.webp differ diff --git a/public/og/thumb/patching-workbench-26-for-mariadb-400.webp b/public/og/thumb/patching-workbench-26-for-mariadb-400.webp new file mode 100644 index 0000000..6a7d0a4 Binary files /dev/null and b/public/og/thumb/patching-workbench-26-for-mariadb-400.webp differ diff --git a/public/og/thumb/patching-workbench-26-for-mariadb-800.webp b/public/og/thumb/patching-workbench-26-for-mariadb-800.webp new file mode 100644 index 0000000..86bfe06 Binary files /dev/null and b/public/og/thumb/patching-workbench-26-for-mariadb-800.webp differ diff --git a/public/og/thumb/putting-a-front-door-on-an-app-you-cant-modify-400.webp b/public/og/thumb/putting-a-front-door-on-an-app-you-cant-modify-400.webp new file mode 100644 index 0000000..294c9ed Binary files /dev/null and b/public/og/thumb/putting-a-front-door-on-an-app-you-cant-modify-400.webp differ diff --git a/public/og/thumb/putting-a-front-door-on-an-app-you-cant-modify-800.webp b/public/og/thumb/putting-a-front-door-on-an-app-you-cant-modify-800.webp new file mode 100644 index 0000000..34cb88e Binary files /dev/null and b/public/og/thumb/putting-a-front-door-on-an-app-you-cant-modify-800.webp differ diff --git a/public/og/thumb/read-only-nocodb-dashboard-for-a-remote-database-400.webp b/public/og/thumb/read-only-nocodb-dashboard-for-a-remote-database-400.webp new file mode 100644 index 0000000..8f7b593 Binary files /dev/null and b/public/og/thumb/read-only-nocodb-dashboard-for-a-remote-database-400.webp differ diff --git a/public/og/thumb/read-only-nocodb-dashboard-for-a-remote-database-800.webp b/public/og/thumb/read-only-nocodb-dashboard-for-a-remote-database-800.webp new file mode 100644 index 0000000..4a944e0 Binary files /dev/null and b/public/og/thumb/read-only-nocodb-dashboard-for-a-remote-database-800.webp differ diff --git a/public/og/thumb/reading-a-containers-own-api-docs-400.webp b/public/og/thumb/reading-a-containers-own-api-docs-400.webp new file mode 100644 index 0000000..b2f9077 Binary files /dev/null and b/public/og/thumb/reading-a-containers-own-api-docs-400.webp differ diff --git a/public/og/thumb/reading-a-containers-own-api-docs-800.webp b/public/og/thumb/reading-a-containers-own-api-docs-800.webp new file mode 100644 index 0000000..ab75209 Binary files /dev/null and b/public/og/thumb/reading-a-containers-own-api-docs-800.webp differ diff --git a/public/og/thumb/replacing-rdpguard-with-ipban-400.webp b/public/og/thumb/replacing-rdpguard-with-ipban-400.webp new file mode 100644 index 0000000..bfacf77 Binary files /dev/null and b/public/og/thumb/replacing-rdpguard-with-ipban-400.webp differ diff --git a/public/og/thumb/replacing-rdpguard-with-ipban-800.webp b/public/og/thumb/replacing-rdpguard-with-ipban-800.webp new file mode 100644 index 0000000..f2b0722 Binary files /dev/null and b/public/og/thumb/replacing-rdpguard-with-ipban-800.webp differ diff --git a/public/og/thumb/running-production-infrastructure-solo-400.webp b/public/og/thumb/running-production-infrastructure-solo-400.webp new file mode 100644 index 0000000..b4ca5ed Binary files /dev/null and b/public/og/thumb/running-production-infrastructure-solo-400.webp differ diff --git a/public/og/thumb/running-production-infrastructure-solo-800.webp b/public/og/thumb/running-production-infrastructure-solo-800.webp new file mode 100644 index 0000000..e9e17f7 Binary files /dev/null and b/public/og/thumb/running-production-infrastructure-solo-800.webp differ diff --git a/public/og/thumb/running-tts-as-a-service-with-token-sidecars-400.webp b/public/og/thumb/running-tts-as-a-service-with-token-sidecars-400.webp new file mode 100644 index 0000000..cdc5200 Binary files /dev/null and b/public/og/thumb/running-tts-as-a-service-with-token-sidecars-400.webp differ diff --git a/public/og/thumb/running-tts-as-a-service-with-token-sidecars-800.webp b/public/og/thumb/running-tts-as-a-service-with-token-sidecars-800.webp new file mode 100644 index 0000000..5e4ea71 Binary files /dev/null and b/public/og/thumb/running-tts-as-a-service-with-token-sidecars-800.webp differ diff --git a/public/og/thumb/scraping-bot-walled-marketplace-warm-browser-session-400.webp b/public/og/thumb/scraping-bot-walled-marketplace-warm-browser-session-400.webp new file mode 100644 index 0000000..241b195 Binary files /dev/null and b/public/og/thumb/scraping-bot-walled-marketplace-warm-browser-session-400.webp differ diff --git a/public/og/thumb/scraping-bot-walled-marketplace-warm-browser-session-800.webp b/public/og/thumb/scraping-bot-walled-marketplace-warm-browser-session-800.webp new file mode 100644 index 0000000..48feca8 Binary files /dev/null and b/public/og/thumb/scraping-bot-walled-marketplace-warm-browser-session-800.webp differ diff --git a/public/og/thumb/self-healing-digital-goods-entitlements-400.webp b/public/og/thumb/self-healing-digital-goods-entitlements-400.webp new file mode 100644 index 0000000..fe05fe1 Binary files /dev/null and b/public/og/thumb/self-healing-digital-goods-entitlements-400.webp differ diff --git a/public/og/thumb/self-healing-digital-goods-entitlements-800.webp b/public/og/thumb/self-healing-digital-goods-entitlements-800.webp new file mode 100644 index 0000000..8f1f9a3 Binary files /dev/null and b/public/og/thumb/self-healing-digital-goods-entitlements-800.webp differ diff --git a/public/og/thumb/self-hosted-speech-to-text-api-400.webp b/public/og/thumb/self-hosted-speech-to-text-api-400.webp new file mode 100644 index 0000000..b61f0b4 Binary files /dev/null and b/public/og/thumb/self-hosted-speech-to-text-api-400.webp differ diff --git a/public/og/thumb/self-hosted-speech-to-text-api-800.webp b/public/og/thumb/self-hosted-speech-to-text-api-800.webp new file mode 100644 index 0000000..6d06fef Binary files /dev/null and b/public/og/thumb/self-hosted-speech-to-text-api-800.webp differ diff --git a/public/og/thumb/self-hosting-mem0-memory-stack-400.webp b/public/og/thumb/self-hosting-mem0-memory-stack-400.webp new file mode 100644 index 0000000..01b59b9 Binary files /dev/null and b/public/og/thumb/self-hosting-mem0-memory-stack-400.webp differ diff --git a/public/og/thumb/self-hosting-mem0-memory-stack-800.webp b/public/og/thumb/self-hosting-mem0-memory-stack-800.webp new file mode 100644 index 0000000..c2cd83b Binary files /dev/null and b/public/og/thumb/self-hosting-mem0-memory-stack-800.webp differ diff --git a/public/og/thumb/shipping-an-ai-photo-editor-as-a-wordpress-plugin-400.webp b/public/og/thumb/shipping-an-ai-photo-editor-as-a-wordpress-plugin-400.webp new file mode 100644 index 0000000..4bbed37 Binary files /dev/null and b/public/og/thumb/shipping-an-ai-photo-editor-as-a-wordpress-plugin-400.webp differ diff --git a/public/og/thumb/shipping-an-ai-photo-editor-as-a-wordpress-plugin-800.webp b/public/og/thumb/shipping-an-ai-photo-editor-as-a-wordpress-plugin-800.webp new file mode 100644 index 0000000..4376585 Binary files /dev/null and b/public/og/thumb/shipping-an-ai-photo-editor-as-a-wordpress-plugin-800.webp differ diff --git a/public/og/thumb/smartctl-exit-code-32-skips-the-disks-that-matter-400.webp b/public/og/thumb/smartctl-exit-code-32-skips-the-disks-that-matter-400.webp new file mode 100644 index 0000000..6b95a29 Binary files /dev/null and b/public/og/thumb/smartctl-exit-code-32-skips-the-disks-that-matter-400.webp differ diff --git a/public/og/thumb/smartctl-exit-code-32-skips-the-disks-that-matter-800.webp b/public/og/thumb/smartctl-exit-code-32-skips-the-disks-that-matter-800.webp new file mode 100644 index 0000000..44d81d2 Binary files /dev/null and b/public/og/thumb/smartctl-exit-code-32-skips-the-disks-that-matter-800.webp differ diff --git a/public/og/thumb/space-reclamation-wasnt-stuck-it-was-waiting-400.webp b/public/og/thumb/space-reclamation-wasnt-stuck-it-was-waiting-400.webp new file mode 100644 index 0000000..cf5599d Binary files /dev/null and b/public/og/thumb/space-reclamation-wasnt-stuck-it-was-waiting-400.webp differ diff --git a/public/og/thumb/space-reclamation-wasnt-stuck-it-was-waiting-800.webp b/public/og/thumb/space-reclamation-wasnt-stuck-it-was-waiting-800.webp new file mode 100644 index 0000000..bdb13fa Binary files /dev/null and b/public/og/thumb/space-reclamation-wasnt-stuck-it-was-waiting-800.webp differ diff --git a/public/og/thumb/stop-writing-agent-prompts-for-deterministic-work-400.webp b/public/og/thumb/stop-writing-agent-prompts-for-deterministic-work-400.webp new file mode 100644 index 0000000..6dffe79 Binary files /dev/null and b/public/og/thumb/stop-writing-agent-prompts-for-deterministic-work-400.webp differ diff --git a/public/og/thumb/stop-writing-agent-prompts-for-deterministic-work-800.webp b/public/og/thumb/stop-writing-agent-prompts-for-deterministic-work-800.webp new file mode 100644 index 0000000..a345e6b Binary files /dev/null and b/public/og/thumb/stop-writing-agent-prompts-for-deterministic-work-800.webp differ diff --git a/public/og/thumb/syncing-a-self-improving-ai-agent-across-machines-400.webp b/public/og/thumb/syncing-a-self-improving-ai-agent-across-machines-400.webp new file mode 100644 index 0000000..792cf0d Binary files /dev/null and b/public/og/thumb/syncing-a-self-improving-ai-agent-across-machines-400.webp differ diff --git a/public/og/thumb/syncing-a-self-improving-ai-agent-across-machines-800.webp b/public/og/thumb/syncing-a-self-improving-ai-agent-across-machines-800.webp new file mode 100644 index 0000000..340011a Binary files /dev/null and b/public/og/thumb/syncing-a-self-improving-ai-agent-across-machines-800.webp differ diff --git a/public/og/thumb/synology-api-401-with-the-correct-password-400.webp b/public/og/thumb/synology-api-401-with-the-correct-password-400.webp new file mode 100644 index 0000000..a448845 Binary files /dev/null and b/public/og/thumb/synology-api-401-with-the-correct-password-400.webp differ diff --git a/public/og/thumb/synology-api-401-with-the-correct-password-800.webp b/public/og/thumb/synology-api-401-with-the-correct-password-800.webp new file mode 100644 index 0000000..c445c61 Binary files /dev/null and b/public/og/thumb/synology-api-401-with-the-correct-password-800.webp differ diff --git a/public/og/thumb/synology-spreadsheet-api-is-a-container-400.webp b/public/og/thumb/synology-spreadsheet-api-is-a-container-400.webp new file mode 100644 index 0000000..ab28fb8 Binary files /dev/null and b/public/og/thumb/synology-spreadsheet-api-is-a-container-400.webp differ diff --git a/public/og/thumb/synology-spreadsheet-api-is-a-container-800.webp b/public/og/thumb/synology-spreadsheet-api-is-a-container-800.webp new file mode 100644 index 0000000..ca9a773 Binary files /dev/null and b/public/og/thumb/synology-spreadsheet-api-is-a-container-800.webp differ diff --git a/public/og/thumb/that-dying-ssd-was-just-a-bad-sata-cable-400.webp b/public/og/thumb/that-dying-ssd-was-just-a-bad-sata-cable-400.webp new file mode 100644 index 0000000..e2c1f7a Binary files /dev/null and b/public/og/thumb/that-dying-ssd-was-just-a-bad-sata-cable-400.webp differ diff --git a/public/og/thumb/that-dying-ssd-was-just-a-bad-sata-cable-800.webp b/public/og/thumb/that-dying-ssd-was-just-a-bad-sata-cable-800.webp new file mode 100644 index 0000000..99dc304 Binary files /dev/null and b/public/og/thumb/that-dying-ssd-was-just-a-bad-sata-cable-800.webp differ diff --git a/public/og/thumb/the-cause-was-trim-not-the-ssds-400.webp b/public/og/thumb/the-cause-was-trim-not-the-ssds-400.webp new file mode 100644 index 0000000..934e4fb Binary files /dev/null and b/public/og/thumb/the-cause-was-trim-not-the-ssds-400.webp differ diff --git a/public/og/thumb/the-cause-was-trim-not-the-ssds-800.webp b/public/og/thumb/the-cause-was-trim-not-the-ssds-800.webp new file mode 100644 index 0000000..2f1d987 Binary files /dev/null and b/public/og/thumb/the-cause-was-trim-not-the-ssds-800.webp differ diff --git a/public/og/thumb/the-nocodb-attachment-that-wouldnt-update-400.webp b/public/og/thumb/the-nocodb-attachment-that-wouldnt-update-400.webp new file mode 100644 index 0000000..8ac0b0b Binary files /dev/null and b/public/og/thumb/the-nocodb-attachment-that-wouldnt-update-400.webp differ diff --git a/public/og/thumb/the-nocodb-attachment-that-wouldnt-update-800.webp b/public/og/thumb/the-nocodb-attachment-that-wouldnt-update-800.webp new file mode 100644 index 0000000..9218133 Binary files /dev/null and b/public/og/thumb/the-nocodb-attachment-that-wouldnt-update-800.webp differ diff --git a/public/og/thumb/the-upstream-was-deleted-then-came-back-rewritten-400.webp b/public/og/thumb/the-upstream-was-deleted-then-came-back-rewritten-400.webp new file mode 100644 index 0000000..c1e9655 Binary files /dev/null and b/public/og/thumb/the-upstream-was-deleted-then-came-back-rewritten-400.webp differ diff --git a/public/og/thumb/the-upstream-was-deleted-then-came-back-rewritten-800.webp b/public/og/thumb/the-upstream-was-deleted-then-came-back-rewritten-800.webp new file mode 100644 index 0000000..d94cc0a Binary files /dev/null and b/public/og/thumb/the-upstream-was-deleted-then-came-back-rewritten-800.webp differ diff --git a/public/og/thumb/two-ways-a-javascript-file-stops-working-and-looks-fine-400.webp b/public/og/thumb/two-ways-a-javascript-file-stops-working-and-looks-fine-400.webp new file mode 100644 index 0000000..47935cd Binary files /dev/null and b/public/og/thumb/two-ways-a-javascript-file-stops-working-and-looks-fine-400.webp differ diff --git a/public/og/thumb/two-ways-a-javascript-file-stops-working-and-looks-fine-800.webp b/public/og/thumb/two-ways-a-javascript-file-stops-working-and-looks-fine-800.webp new file mode 100644 index 0000000..60e2d40 Binary files /dev/null and b/public/og/thumb/two-ways-a-javascript-file-stops-working-and-looks-fine-800.webp differ diff --git a/public/og/thumb/unraid-stop-array-hangs-on-swapfile-400.webp b/public/og/thumb/unraid-stop-array-hangs-on-swapfile-400.webp new file mode 100644 index 0000000..0c19136 Binary files /dev/null and b/public/og/thumb/unraid-stop-array-hangs-on-swapfile-400.webp differ diff --git a/public/og/thumb/unraid-stop-array-hangs-on-swapfile-800.webp b/public/og/thumb/unraid-stop-array-hangs-on-swapfile-800.webp new file mode 100644 index 0000000..a524019 Binary files /dev/null and b/public/og/thumb/unraid-stop-array-hangs-on-swapfile-800.webp differ diff --git a/public/og/thumb/upgrading-codeigniter-46-to-47-400.webp b/public/og/thumb/upgrading-codeigniter-46-to-47-400.webp new file mode 100644 index 0000000..4d44711 Binary files /dev/null and b/public/og/thumb/upgrading-codeigniter-46-to-47-400.webp differ diff --git a/public/og/thumb/upgrading-codeigniter-46-to-47-800.webp b/public/og/thumb/upgrading-codeigniter-46-to-47-800.webp new file mode 100644 index 0000000..9da4a6a Binary files /dev/null and b/public/og/thumb/upgrading-codeigniter-46-to-47-800.webp differ diff --git a/public/og/thumb/using-chinese-llm-apis-from-malaysia-400.webp b/public/og/thumb/using-chinese-llm-apis-from-malaysia-400.webp new file mode 100644 index 0000000..002d216 Binary files /dev/null and b/public/og/thumb/using-chinese-llm-apis-from-malaysia-400.webp differ diff --git a/public/og/thumb/using-chinese-llm-apis-from-malaysia-800.webp b/public/og/thumb/using-chinese-llm-apis-from-malaysia-800.webp new file mode 100644 index 0000000..277a0d8 Binary files /dev/null and b/public/og/thumb/using-chinese-llm-apis-from-malaysia-800.webp differ diff --git a/public/og/thumb/verifying-a-pdf-report-page-by-page-400.webp b/public/og/thumb/verifying-a-pdf-report-page-by-page-400.webp new file mode 100644 index 0000000..ece876d Binary files /dev/null and b/public/og/thumb/verifying-a-pdf-report-page-by-page-400.webp differ diff --git a/public/og/thumb/verifying-a-pdf-report-page-by-page-800.webp b/public/og/thumb/verifying-a-pdf-report-page-by-page-800.webp new file mode 100644 index 0000000..fd84e58 Binary files /dev/null and b/public/og/thumb/verifying-a-pdf-report-page-by-page-800.webp differ diff --git a/public/og/thumb/vetting-an-open-source-dependency-before-you-bet-on-it-400.webp b/public/og/thumb/vetting-an-open-source-dependency-before-you-bet-on-it-400.webp new file mode 100644 index 0000000..a069dcf Binary files /dev/null and b/public/og/thumb/vetting-an-open-source-dependency-before-you-bet-on-it-400.webp differ diff --git a/public/og/thumb/vetting-an-open-source-dependency-before-you-bet-on-it-800.webp b/public/og/thumb/vetting-an-open-source-dependency-before-you-bet-on-it-800.webp new file mode 100644 index 0000000..b26647b Binary files /dev/null and b/public/og/thumb/vetting-an-open-source-dependency-before-you-bet-on-it-800.webp differ diff --git a/public/og/thumb/when-smart-says-healthy-but-your-raid-is-corrupting-data-400.webp b/public/og/thumb/when-smart-says-healthy-but-your-raid-is-corrupting-data-400.webp new file mode 100644 index 0000000..6ddf450 Binary files /dev/null and b/public/og/thumb/when-smart-says-healthy-but-your-raid-is-corrupting-data-400.webp differ diff --git a/public/og/thumb/when-smart-says-healthy-but-your-raid-is-corrupting-data-800.webp b/public/og/thumb/when-smart-says-healthy-but-your-raid-is-corrupting-data-800.webp new file mode 100644 index 0000000..2849b6e Binary files /dev/null and b/public/og/thumb/when-smart-says-healthy-but-your-raid-is-corrupting-data-800.webp differ diff --git a/public/og/thumb/why-chrome-forgets-its-tabs-in-a-container-400.webp b/public/og/thumb/why-chrome-forgets-its-tabs-in-a-container-400.webp new file mode 100644 index 0000000..b53edaf Binary files /dev/null and b/public/og/thumb/why-chrome-forgets-its-tabs-in-a-container-400.webp differ diff --git a/public/og/thumb/why-chrome-forgets-its-tabs-in-a-container-800.webp b/public/og/thumb/why-chrome-forgets-its-tabs-in-a-container-800.webp new file mode 100644 index 0000000..e163ee2 Binary files /dev/null and b/public/og/thumb/why-chrome-forgets-its-tabs-in-a-container-800.webp differ diff --git a/public/og/thumb/why-i-still-bought-a-local-gpu-400.webp b/public/og/thumb/why-i-still-bought-a-local-gpu-400.webp new file mode 100644 index 0000000..b5c051f Binary files /dev/null and b/public/og/thumb/why-i-still-bought-a-local-gpu-400.webp differ diff --git a/public/og/thumb/why-i-still-bought-a-local-gpu-800.webp b/public/og/thumb/why-i-still-bought-a-local-gpu-800.webp new file mode 100644 index 0000000..bff24e2 Binary files /dev/null and b/public/og/thumb/why-i-still-bought-a-local-gpu-800.webp differ diff --git a/public/og/thumb/why-my-on-chain-nft-art-changed-on-windows-400.webp b/public/og/thumb/why-my-on-chain-nft-art-changed-on-windows-400.webp new file mode 100644 index 0000000..d93ed80 Binary files /dev/null and b/public/og/thumb/why-my-on-chain-nft-art-changed-on-windows-400.webp differ diff --git a/public/og/thumb/why-my-on-chain-nft-art-changed-on-windows-800.webp b/public/og/thumb/why-my-on-chain-nft-art-changed-on-windows-800.webp new file mode 100644 index 0000000..d1f4954 Binary files /dev/null and b/public/og/thumb/why-my-on-chain-nft-art-changed-on-windows-800.webp differ diff --git a/public/og/thumb/why-telegram-bot-notifications-die-400.webp b/public/og/thumb/why-telegram-bot-notifications-die-400.webp new file mode 100644 index 0000000..5f67d27 Binary files /dev/null and b/public/og/thumb/why-telegram-bot-notifications-die-400.webp differ diff --git a/public/og/thumb/why-telegram-bot-notifications-die-800.webp b/public/og/thumb/why-telegram-bot-notifications-die-800.webp new file mode 100644 index 0000000..eca2e60 Binary files /dev/null and b/public/og/thumb/why-telegram-bot-notifications-die-800.webp differ diff --git a/public/og/thumb/why-your-cdp-clicks-silently-fail-400.webp b/public/og/thumb/why-your-cdp-clicks-silently-fail-400.webp new file mode 100644 index 0000000..3719acc Binary files /dev/null and b/public/og/thumb/why-your-cdp-clicks-silently-fail-400.webp differ diff --git a/public/og/thumb/why-your-cdp-clicks-silently-fail-800.webp b/public/og/thumb/why-your-cdp-clicks-silently-fail-800.webp new file mode 100644 index 0000000..ed2b4e4 Binary files /dev/null and b/public/og/thumb/why-your-cdp-clicks-silently-fail-800.webp differ diff --git a/public/og/thumb/why-your-grafana-dashboard-shows-no-data-400.webp b/public/og/thumb/why-your-grafana-dashboard-shows-no-data-400.webp new file mode 100644 index 0000000..03766e7 Binary files /dev/null and b/public/og/thumb/why-your-grafana-dashboard-shows-no-data-400.webp differ diff --git a/public/og/thumb/why-your-grafana-dashboard-shows-no-data-800.webp b/public/og/thumb/why-your-grafana-dashboard-shows-no-data-800.webp new file mode 100644 index 0000000..eea5530 Binary files /dev/null and b/public/og/thumb/why-your-grafana-dashboard-shows-no-data-800.webp differ diff --git a/public/og/thumb/why-your-headless-browser-cant-scrape-everything-400.webp b/public/og/thumb/why-your-headless-browser-cant-scrape-everything-400.webp new file mode 100644 index 0000000..e734557 Binary files /dev/null and b/public/og/thumb/why-your-headless-browser-cant-scrape-everything-400.webp differ diff --git a/public/og/thumb/why-your-headless-browser-cant-scrape-everything-800.webp b/public/og/thumb/why-your-headless-browser-cant-scrape-everything-800.webp new file mode 100644 index 0000000..ea40435 Binary files /dev/null and b/public/og/thumb/why-your-headless-browser-cant-scrape-everything-800.webp differ diff --git a/public/og/thumb/your-disk-full-alert-is-lying-400.webp b/public/og/thumb/your-disk-full-alert-is-lying-400.webp new file mode 100644 index 0000000..4fe4fee Binary files /dev/null and b/public/og/thumb/your-disk-full-alert-is-lying-400.webp differ diff --git a/public/og/thumb/your-disk-full-alert-is-lying-800.webp b/public/og/thumb/your-disk-full-alert-is-lying-800.webp new file mode 100644 index 0000000..d0add4b Binary files /dev/null and b/public/og/thumb/your-disk-full-alert-is-lying-800.webp differ diff --git a/scripts/optimize-images.py b/scripts/optimize-images.py new file mode 100644 index 0000000..949550c --- /dev/null +++ b/scripts/optimize-images.py @@ -0,0 +1,74 @@ +#!/usr/bin/env python3 +"""Generate the derivative images the site serves in-page. + +Three outputs, all derived from the existing PNGs: + +1. `public/banners/.webp` article banner, q78 (~42% of the PNG) +2. `public/og/thumb/-800.webp` list thumbnail @2x, q72 +3. `public/og/thumb/-400.webp` list thumbnail @1x, q72 + +Why not JPEG anywhere: this art is flat terminal-style with sharp text edges, and it +measured *larger* as JPEG q85 than as PNG (129%); WebP came out at 42%. The PNGs stay +the source of truth for `og:image` (social scrapers do not reliably render WebP share +cards), and the banner PNG doubles as the fallback. + +Usage: + uv run --with pillow python scripts/optimize-images.py # everything + uv run --with pillow python scripts/optimize-images.py ... # one post +""" +import glob, os, sys +from PIL import Image + +ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) +BANNERS = os.path.join(ROOT, "public", "banners") +OG = os.path.join(ROOT, "public", "og") +THUMBS = os.path.join(OG, "thumb") +BANNER_Q = 78 +THUMB_Q = 72 + +def save_webp(im, path, quality): + os.makedirs(os.path.dirname(path), exist_ok=True) + if im.mode not in ("RGB", "RGBA"): + im = im.convert("RGBA" if "A" in im.mode else "RGB") + im.save(path, "WEBP", quality=quality, method=6) + return os.path.getsize(path) + +def banner(slug): + png = os.path.join(BANNERS, slug + ".png") + if not os.path.exists(png): + return None + return os.path.getsize(png), save_webp(Image.open(png), png[:-4] + ".webp", BANNER_Q) + +def thumbs(slug): + png = os.path.join(OG, slug + ".png") + if not os.path.exists(png): + return None + im = Image.open(png) + src = os.path.getsize(png) + out = [] + for w in (400, 800): + h = round(w * 630 / 1200) + out.append(save_webp(im.resize((w, h), Image.LANCZOS), + os.path.join(THUMBS, f"{slug}-{w}.webp"), THUMB_Q)) + return src, out + +targets = sys.argv[1:] +slugs = targets or sorted(os.path.basename(p)[:-4] for p in glob.glob(os.path.join(OG, "*.png")) + if not p.endswith("og-default.png")) + +b_png = b_webp = 0 +t_png = t_400 = t_800 = 0 +for s in slugs: + b = banner(s) + if b: + b_png += b[0]; b_webp += b[1] + t = thumbs(s) + if t: + t_png += t[0]; t_400 += t[1][0]; t_800 += t[1][1] + +n = max(1, len(slugs)) +print(f"banners : {len(slugs)} files {b_png/1024/1024:.2f} MB PNG -> {b_webp/1024/1024:.2f} MB WebP " + f"({100*b_webp/max(1,b_png):.0f}%) [{b_webp/n/1024:.1f} KB/file]") +print(f"thumbs : 400w {t_400/1024:.0f} KB ({t_400/n/1024:.1f} KB/file) + 800w " + f"{t_800/1024:.0f} KB ({t_800/n/1024:.1f} KB/file) vs PNG {t_png/1024/1024:.2f} MB " + f"({100*(t_400+t_800)/max(1,t_png):.0f}% of PNG)") diff --git a/src/components/PostList.astro b/src/components/PostList.astro index 0cbdd8f..17312ca 100644 --- a/src/components/PostList.astro +++ b/src/components/PostList.astro @@ -23,13 +23,32 @@ const updatedLabel = isZh ? '更新于' : 'Updated'; const catHref = (cat: string) => `/${isZh ? 'zh/categories' : 'categories'}/${cat}/`; const og = (post: CollectionEntry<'posts'>) => post.data.ogImage || '/og-default.png'; + +// The row thumbnail is displayed at ~398 CSS px wide, so the list used to pull a +// 1200x630 PNG (~45 KB) per row for a 398px slot. Serve 400w/800w WebP derived from +// the same art instead (5.8 KB / 16.1 KB — see scripts/optimize-images.py). +const hasThumb = (post: CollectionEntry<'posts'>) => { + const p = og(post); + return p.startsWith('/og/') && p !== '/og-default.png'; +}; +const thumb = (post: CollectionEntry<'posts'>, w: 400 | 800) => + og(post).replace('/og/', '/og/thumb/').replace(/\.png$/, `-${w}.webp`); ---
    {shown.map((post) => (
  • {post.data.title}

    diff --git a/src/pages/posts/[...slug].astro b/src/pages/posts/[...slug].astro index 3f96372..4380d9f 100644 --- a/src/pages/posts/[...slug].astro +++ b/src/pages/posts/[...slug].astro @@ -25,6 +25,8 @@ const minutes = Math.max(1, Math.round(words / 200)); const fmt = (d: Date) => d.toLocaleDateString(lang === 'zh' ? 'zh-CN' : 'en-US', { year: 'numeric', month: 'long', day: 'numeric' }); +const minutesOf = (body?: string) => Math.max(1, Math.round((body?.trim().split(/\s+/).length ?? 0) / 200)); + // Related posts: same language, same category or overlapping tags, excluding self const all = await getCollection('posts', ({ data }) => !data.draft); const related = all @@ -101,7 +103,12 @@ const jsonLd = { {post.data.banner && (
    - + {/* WebP for the page (42% of the PNG bytes); the PNG stays for og:image + because social scrapers do not reliably render WebP share cards. */} + + + +
    )} @@ -116,10 +123,14 @@ const jsonLd = {