From 1e27dbe5535b97794164ace3fdf1d2cb9acea2fb Mon Sep 17 00:00:00 2001 From: Eason WaveKat Date: Mon, 30 Mar 2026 18:26:57 +1300 Subject: [PATCH 1/3] feat: add Open Graph meta tags for social sharing Sync og.png from wavekat-brand and add OG/Twitter meta tags so LinkedIn and other platforms show a rich preview when the site is shared. Co-Authored-By: Claude Opus 4.6 (1M context) --- .gitignore | 1 + Makefile | 2 +- astro.config.mjs | 1 + scripts/sync-brand.js | 22 ++++++++++++++++------ src/layouts/Base.astro | 21 +++++++++++++++++++++ 5 files changed, 40 insertions(+), 7 deletions(-) diff --git a/.gitignore b/.gitignore index f6ffcd5..4527f48 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,7 @@ dist/ # Generated from vendor/wavekat-brand submodule (run `make sync`) /public/logos/ +/public/og.png # Environment .env diff --git a/Makefile b/Makefile index 5b0f33c..5d00333 100644 --- a/Makefile +++ b/Makefile @@ -29,7 +29,7 @@ cf-build: # Remove build artifacts and synced assets clean: - rm -rf dist/ .astro/ public/logos/ + rm -rf dist/ .astro/ public/logos/ public/og.png help: @echo "Usage: make " diff --git a/astro.config.mjs b/astro.config.mjs index 926abcb..b5bc8e6 100644 --- a/astro.config.mjs +++ b/astro.config.mjs @@ -5,6 +5,7 @@ import tailwindcss from '@tailwindcss/vite'; // https://astro.build/config export default defineConfig({ + site: 'https://wavekat.com', output: 'static', vite: { plugins: [tailwindcss()], diff --git a/scripts/sync-brand.js b/scripts/sync-brand.js index 648d1af..55265cd 100644 --- a/scripts/sync-brand.js +++ b/scripts/sync-brand.js @@ -11,15 +11,20 @@ import { join } from "path"; import { fileURLToPath } from "url"; const root = join(fileURLToPath(import.meta.url), "../.."); -const src = join(root, "vendor/wavekat-brand/assets/logos"); -const dest = join(root, "public/logos"); +const brandDir = join(root, "vendor/wavekat-brand/assets"); +const logoSrc = join(brandDir, "logos"); +const logoDest = join(root, "public/logos"); -const assets = [ +const logos = [ "wavekat-tight-light.svg", "wavekat-tight-dark.svg", "wavekat-icon-light.svg", ]; +const rootAssets = [ + { src: "og.png", dest: join(root, "public/og.png") }, +]; + // Initialise submodule if vendor directory is empty (Cloudflare Pages shallow clone) try { execSync("git submodule update --init --recursive", { @@ -30,9 +35,14 @@ try { // Not a fatal error — submodule may already be present } -mkdirSync(dest, { recursive: true }); +mkdirSync(logoDest, { recursive: true }); -for (const file of assets) { - cpSync(join(src, file), join(dest, file)); +for (const file of logos) { + cpSync(join(logoSrc, file), join(logoDest, file)); console.log(`synced ${file}`); } + +for (const { src, dest } of rootAssets) { + cpSync(join(brandDir, src), dest); + console.log(`synced ${src}`); +} diff --git a/src/layouts/Base.astro b/src/layouts/Base.astro index e69005d..ed120c2 100644 --- a/src/layouts/Base.astro +++ b/src/layouts/Base.astro @@ -4,12 +4,17 @@ import '../styles/global.css'; interface Props { title?: string; description?: string; + ogImage?: string; } const { title = 'WaveKat', description = 'Give every small business the voice of a big one.', + ogImage = '/og.png', } = Astro.props; + +const canonicalURL = new URL(Astro.url.pathname, Astro.site); +const ogImageURL = new URL(ogImage, Astro.site); --- @@ -18,8 +23,24 @@ const { + {title} + + + + + + + + + + + + + + + From e5cdfead914d2afd090d01ef6697516d064d8f73 Mon Sep 17 00:00:00 2001 From: Eason WaveKat Date: Mon, 30 Mar 2026 18:36:27 +1300 Subject: [PATCH 2/3] fix: convert og.svg to PNG at build time with resvg Use @resvg/resvg-js (WASM) to convert the SVG from wavekat-brand to PNG during sync, instead of expecting a pre-built og.png. Co-Authored-By: Claude Opus 4.6 (1M context) --- package-lock.json | 216 ++++++++++++++++++++++++++++++++++++++++++ package.json | 1 + scripts/sync-brand.js | 16 ++-- 3 files changed, 224 insertions(+), 9 deletions(-) diff --git a/package-lock.json b/package-lock.json index 6c3603b..1025e34 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,7 @@ "name": "wavekat-com", "version": "0.0.2", "dependencies": { + "@resvg/resvg-js": "^2.6.2", "@tailwindcss/vite": "^4.2.2", "astro": "^6.1.1", "tailwindcss": "^4.2.2" @@ -1280,6 +1281,221 @@ "dev": true, "license": "MIT" }, + "node_modules/@resvg/resvg-js": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/@resvg/resvg-js/-/resvg-js-2.6.2.tgz", + "integrity": "sha512-xBaJish5OeGmniDj9cW5PRa/PtmuVU3ziqrbr5xJj901ZDN4TosrVaNZpEiLZAxdfnhAe7uQ7QFWfjPe9d9K2Q==", + "license": "MPL-2.0", + "engines": { + "node": ">= 10" + }, + "optionalDependencies": { + "@resvg/resvg-js-android-arm-eabi": "2.6.2", + "@resvg/resvg-js-android-arm64": "2.6.2", + "@resvg/resvg-js-darwin-arm64": "2.6.2", + "@resvg/resvg-js-darwin-x64": "2.6.2", + "@resvg/resvg-js-linux-arm-gnueabihf": "2.6.2", + "@resvg/resvg-js-linux-arm64-gnu": "2.6.2", + "@resvg/resvg-js-linux-arm64-musl": "2.6.2", + "@resvg/resvg-js-linux-x64-gnu": "2.6.2", + "@resvg/resvg-js-linux-x64-musl": "2.6.2", + "@resvg/resvg-js-win32-arm64-msvc": "2.6.2", + "@resvg/resvg-js-win32-ia32-msvc": "2.6.2", + "@resvg/resvg-js-win32-x64-msvc": "2.6.2" + } + }, + "node_modules/@resvg/resvg-js-android-arm-eabi": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/@resvg/resvg-js-android-arm-eabi/-/resvg-js-android-arm-eabi-2.6.2.tgz", + "integrity": "sha512-FrJibrAk6v29eabIPgcTUMPXiEz8ssrAk7TXxsiZzww9UTQ1Z5KAbFJs+Z0Ez+VZTYgnE5IQJqBcoSiMebtPHA==", + "cpu": [ + "arm" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@resvg/resvg-js-android-arm64": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/@resvg/resvg-js-android-arm64/-/resvg-js-android-arm64-2.6.2.tgz", + "integrity": "sha512-VcOKezEhm2VqzXpcIJoITuvUS/fcjIw5NA/w3tjzWyzmvoCdd+QXIqy3FBGulWdClvp4g+IfUemigrkLThSjAQ==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@resvg/resvg-js-darwin-arm64": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/@resvg/resvg-js-darwin-arm64/-/resvg-js-darwin-arm64-2.6.2.tgz", + "integrity": "sha512-nmok2LnAd6nLUKI16aEB9ydMC6Lidiiq2m1nEBDR1LaaP7FGs4AJ90qDraxX+CWlVuRlvNjyYJTNv8qFjtL9+A==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@resvg/resvg-js-darwin-x64": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/@resvg/resvg-js-darwin-x64/-/resvg-js-darwin-x64-2.6.2.tgz", + "integrity": "sha512-GInyZLjgWDfsVT6+SHxQVRwNzV0AuA1uqGsOAW+0th56J7Nh6bHHKXHBWzUrihxMetcFDmQMAX1tZ1fZDYSRsw==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@resvg/resvg-js-linux-arm-gnueabihf": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/@resvg/resvg-js-linux-arm-gnueabihf/-/resvg-js-linux-arm-gnueabihf-2.6.2.tgz", + "integrity": "sha512-YIV3u/R9zJbpqTTNwTZM5/ocWetDKGsro0SWp70eGEM9eV2MerWyBRZnQIgzU3YBnSBQ1RcxRZvY/UxwESfZIw==", + "cpu": [ + "arm" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@resvg/resvg-js-linux-arm64-gnu": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/@resvg/resvg-js-linux-arm64-gnu/-/resvg-js-linux-arm64-gnu-2.6.2.tgz", + "integrity": "sha512-zc2BlJSim7YR4FZDQ8OUoJg5holYzdiYMeobb9pJuGDidGL9KZUv7SbiD4E8oZogtYY42UZEap7dqkkYuA91pg==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@resvg/resvg-js-linux-arm64-musl": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/@resvg/resvg-js-linux-arm64-musl/-/resvg-js-linux-arm64-musl-2.6.2.tgz", + "integrity": "sha512-3h3dLPWNgSsD4lQBJPb4f+kvdOSJHa5PjTYVsWHxLUzH4IFTJUAnmuWpw4KqyQ3NA5QCyhw4TWgxk3jRkQxEKg==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@resvg/resvg-js-linux-x64-gnu": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/@resvg/resvg-js-linux-x64-gnu/-/resvg-js-linux-x64-gnu-2.6.2.tgz", + "integrity": "sha512-IVUe+ckIerA7xMZ50duAZzwf1U7khQe2E0QpUxu5MBJNao5RqC0zwV/Zm965vw6D3gGFUl7j4m+oJjubBVoftw==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@resvg/resvg-js-linux-x64-musl": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/@resvg/resvg-js-linux-x64-musl/-/resvg-js-linux-x64-musl-2.6.2.tgz", + "integrity": "sha512-UOf83vqTzoYQO9SZ0fPl2ZIFtNIz/Rr/y+7X8XRX1ZnBYsQ/tTb+cj9TE+KHOdmlTFBxhYzVkP2lRByCzqi4jQ==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@resvg/resvg-js-win32-arm64-msvc": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/@resvg/resvg-js-win32-arm64-msvc/-/resvg-js-win32-arm64-msvc-2.6.2.tgz", + "integrity": "sha512-7C/RSgCa+7vqZ7qAbItfiaAWhyRSoD4l4BQAbVDqRRsRgY+S+hgS3in0Rxr7IorKUpGE69X48q6/nOAuTJQxeQ==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@resvg/resvg-js-win32-ia32-msvc": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/@resvg/resvg-js-win32-ia32-msvc/-/resvg-js-win32-ia32-msvc-2.6.2.tgz", + "integrity": "sha512-har4aPAlvjnLcil40AC77YDIk6loMawuJwFINEM7n0pZviwMkMvjb2W5ZirsNOZY4aDbo5tLx0wNMREp5Brk+w==", + "cpu": [ + "ia32" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@resvg/resvg-js-win32-x64-msvc": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/@resvg/resvg-js-win32-x64-msvc/-/resvg-js-win32-x64-msvc-2.6.2.tgz", + "integrity": "sha512-ZXtYhtUr5SSaBrUDq7DiyjOFJqBVL/dOBN7N/qmi/pO0IgiWW/f/ue3nbvu9joWE5aAKDoIzy/CxsY0suwGosQ==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, "node_modules/@rollup/pluginutils": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.3.0.tgz", diff --git a/package.json b/package.json index 1a0d763..df34c6f 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ "astro": "astro" }, "dependencies": { + "@resvg/resvg-js": "^2.6.2", "@tailwindcss/vite": "^4.2.2", "astro": "^6.1.1", "tailwindcss": "^4.2.2" diff --git a/scripts/sync-brand.js b/scripts/sync-brand.js index 55265cd..aa145e4 100644 --- a/scripts/sync-brand.js +++ b/scripts/sync-brand.js @@ -5,10 +5,11 @@ // npm run cf:build // as the Pages build command. -import { cpSync, mkdirSync } from "fs"; +import { cpSync, mkdirSync, readFileSync, writeFileSync } from "fs"; import { execSync } from "child_process"; import { join } from "path"; import { fileURLToPath } from "url"; +import { Resvg } from "@resvg/resvg-js"; const root = join(fileURLToPath(import.meta.url), "../.."); const brandDir = join(root, "vendor/wavekat-brand/assets"); @@ -21,10 +22,6 @@ const logos = [ "wavekat-icon-light.svg", ]; -const rootAssets = [ - { src: "og.png", dest: join(root, "public/og.png") }, -]; - // Initialise submodule if vendor directory is empty (Cloudflare Pages shallow clone) try { execSync("git submodule update --init --recursive", { @@ -42,7 +39,8 @@ for (const file of logos) { console.log(`synced ${file}`); } -for (const { src, dest } of rootAssets) { - cpSync(join(brandDir, src), dest); - console.log(`synced ${src}`); -} +// Convert og.svg → og.png (social platforms require raster images) +const ogSvg = readFileSync(join(brandDir, "og.svg"), "utf8"); +const resvg = new Resvg(ogSvg, { fitTo: { mode: "width", value: 1200 } }); +writeFileSync(join(root, "public/og.png"), resvg.render().asPng()); +console.log("synced og.svg → og.png"); From 384648a7802789e45accce9132a2e200fb159551 Mon Sep 17 00:00:00 2001 From: Eason WaveKat Date: Mon, 30 Mar 2026 18:41:27 +1300 Subject: [PATCH 3/3] fix: bump wavekat-brand submodule to include og.svg Co-Authored-By: Claude Opus 4.6 (1M context) --- vendor/wavekat-brand | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vendor/wavekat-brand b/vendor/wavekat-brand index 9ca9515..a0c6ce4 160000 --- a/vendor/wavekat-brand +++ b/vendor/wavekat-brand @@ -1 +1 @@ -Subproject commit 9ca951514ca2ef1b6dea9d0250b6030e6fd94050 +Subproject commit a0c6ce4d9e0b06e46db97012c2e1c9c8747bdf27