this repo has no description
10
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat(explore): add social preview image

Use an Explore-specific Open Graph image for registry pages so Bluesky embeds reflect the app directory rather than the homepage.

Made-with: Cursor

+360 -3
+2
deno.lock
··· 41 41 "npm:esbuild@0.25.7": "0.25.7", 42 42 "npm:esbuild@~0.25.5": "0.25.12", 43 43 "npm:png-to-ico@*": "3.0.1", 44 + "npm:png-to-ico@3.0.1": "3.0.1", 44 45 "npm:preact-render-to-string@^6.6.3": "6.6.7_preact@10.29.1", 45 46 "npm:preact@^10.27.2": "10.29.1", 46 47 "npm:preact@^10.28.2": "10.29.1", 47 48 "npm:preact@^10.28.3": "10.29.1", 48 49 "npm:rollup@^4.50.0": "4.60.1", 49 50 "npm:sharp@*": "0.34.5", 51 + "npm:sharp@0.34.5": "0.34.5", 50 52 "npm:vite@^7.1.3": "7.3.2", 51 53 "npm:vite@^7.1.4": "7.3.2" 52 54 },
+9 -3
routes/_app.tsx
··· 254 254 const effectsOn = url.pathname === "/"; 255 255 const htmlClass = effectsOn ? "sky-effects" : "sky-static"; 256 256 const bodyClass = effectsOn ? "sky-bg" : "sky-bg explore-no-effects"; 257 + const socialImagePath = url.pathname.startsWith("/explore") 258 + ? "/og-explore.png" 259 + : "/og-hero.png"; 260 + const socialImageAlt = url.pathname.startsWith("/explore") 261 + ? "Explore the Atmosphere registry: apps, profiles, reviews, and updates." 262 + : t.meta.ogImageAlt; 257 263 return ( 258 264 <html lang={locale} class={htmlClass}> 259 265 <head> ··· 265 271 <meta property="og:description" content={t.meta.ogDescription} /> 266 272 <meta property="og:locale" content={locale} /> 267 273 <meta property="og:type" content="website" /> 268 - <meta property="og:image" content={socialImageUrl("/og-hero.png")} /> 274 + <meta property="og:image" content={socialImageUrl(socialImagePath)} /> 269 275 <meta property="og:image:type" content="image/png" /> 270 276 <meta property="og:image:width" content="1200" /> 271 277 <meta property="og:image:height" content="630" /> 272 - <meta property="og:image:alt" content={t.meta.ogImageAlt} /> 278 + <meta property="og:image:alt" content={socialImageAlt} /> 273 279 <meta name="twitter:card" content="summary_large_image" /> 274 - <meta name="twitter:image" content={socialImageUrl("/og-hero.png")} /> 280 + <meta name="twitter:image" content={socialImageUrl(socialImagePath)} /> 275 281 <link rel="icon" href="/favicon.ico" sizes="any" /> 276 282 <link rel="icon" type="image/svg+xml" href="/union.svg" /> 277 283 <link rel="apple-touch-icon" href="/union.svg" />
+6
scripts/generate-static-images.ts
··· 11 11 await writeFile(join(root, "static/og-hero.png"), ogPng); 12 12 console.log("Wrote static/og-hero.png", ogPng.length, "bytes"); 13 13 14 + const exploreOgSvg = await readFile(join(root, "static/og-explore.svg")); 15 + const exploreOgPng = await sharp(exploreOgSvg).png().resize(1200, 630) 16 + .toBuffer(); 17 + await writeFile(join(root, "static/og-explore.png"), exploreOgPng); 18 + console.log("Wrote static/og-explore.png", exploreOgPng.length, "bytes"); 19 + 14 20 const unionSvg = await readFile(join(root, "static/union.svg")); 15 21 const bg = { r: 0, g: 0, b: 0, alpha: 0 }; 16 22 const icon32 = await sharp(unionSvg).resize(32, 32, {
static/og-explore.png

This is a binary file and will not be displayed.

+343
static/og-explore.svg
··· 1 + <svg 2 + xmlns="http://www.w3.org/2000/svg" 3 + width="1200" 4 + height="630" 5 + viewBox="0 0 1200 630" 6 + fill="none" 7 + > 8 + <defs> 9 + <linearGradient 10 + id="sky" 11 + x1="600" 12 + y1="0" 13 + x2="600" 14 + y2="630" 15 + gradientUnits="userSpaceOnUse" 16 + > 17 + <stop stop-color="#e8f0fe" /> 18 + <stop offset="0.28" stop-color="#c9d8f5" /> 19 + <stop offset="0.58" stop-color="#a8c4f0" /> 20 + <stop offset="1" stop-color="#ebe4f5" /> 21 + </linearGradient> 22 + <radialGradient 23 + id="sun" 24 + cx="0" 25 + cy="0" 26 + r="1" 27 + gradientUnits="userSpaceOnUse" 28 + gradientTransform="translate(250 115) rotate(90) scale(460 360)" 29 + > 30 + <stop stop-color="#fff7d8" stop-opacity="0.92" /> 31 + <stop offset="0.38" stop-color="#ffe5ac" stop-opacity="0.48" /> 32 + <stop offset="1" stop-color="#ffd8a0" stop-opacity="0" /> 33 + </radialGradient> 34 + <linearGradient 35 + id="card" 36 + x1="0" 37 + y1="0" 38 + x2="1" 39 + y2="1" 40 + gradientUnits="objectBoundingBox" 41 + > 42 + <stop stop-color="#ffffff" stop-opacity="0.78" /> 43 + <stop offset="1" stop-color="#ffffff" stop-opacity="0.42" /> 44 + </linearGradient> 45 + <linearGradient 46 + id="cloudStroke" 47 + x1="0" 48 + y1="0" 49 + x2="0" 50 + y2="1" 51 + gradientUnits="objectBoundingBox" 52 + > 53 + <stop stop-color="#D0FCFF" /> 54 + <stop offset="0.78" stop-color="#E9FEFF" stop-opacity="0.55" /> 55 + <stop offset="1" stop-color="#E9FEFF" stop-opacity="0.42" /> 56 + </linearGradient> 57 + <filter 58 + id="blur" 59 + x="-20%" 60 + y="-20%" 61 + width="140%" 62 + height="140%" 63 + color-interpolation-filters="sRGB" 64 + > 65 + <feGaussianBlur stdDeviation="10" result="b" /> 66 + <feMerge> 67 + <feMergeNode in="b" /> 68 + <feMergeNode in="SourceGraphic" /> 69 + </feMerge> 70 + </filter> 71 + <filter 72 + id="shadow" 73 + x="-20%" 74 + y="-20%" 75 + width="140%" 76 + height="140%" 77 + color-interpolation-filters="sRGB" 78 + > 79 + <feDropShadow 80 + dx="0" 81 + dy="18" 82 + stdDeviation="22" 83 + flood-color="#0e1428" 84 + flood-opacity="0.12" 85 + /> 86 + </filter> 87 + </defs> 88 + 89 + <rect width="1200" height="630" fill="url(#sky)" /> 90 + <rect width="1200" height="630" fill="url(#sun)" /> 91 + 92 + <g opacity="0.78" filter="url(#blur)"> 93 + <g transform="translate(-90 245) scale(0.45)"> 94 + <path 95 + fill="#79CFFF" 96 + fill-opacity="0.16" 97 + stroke="url(#cloudStroke)" 98 + stroke-width="4" 99 + stroke-linejoin="round" 100 + d="M490 369.95C491.23 369.98 492.47 370 493.71 370C563.73 370 620 319.6 620 257.3C620 197 564.63 147.7 500.67 144.68C483.44 100.58 436.3 68.93 380.85 68.93C372.72 68.93 364.78 69.61 357.08 70.9C338.4 29.33 292.67 0 238.87 0C168.85 0 112 50.41 112 112.71C112 123.83 113.82 134.58 117.2 144.73C51.65 149.08 0 197.73 0 257.3C0 319.6 56.78 370 126.79 370C128.85 370 130.9 369.96 132.94 369.87V370H490V369.95Z" 101 + /> 102 + </g> 103 + <g transform="translate(790 50) scale(0.38)"> 104 + <path 105 + fill="#79CFFF" 106 + fill-opacity="0.17" 107 + stroke="url(#cloudStroke)" 108 + stroke-width="4" 109 + stroke-linejoin="round" 110 + d="M379 289.96C380 289.98 381.01 290 382.03 290C436.23 290 480 250.57 480 201.63C480 154.21 438.75 115.62 387.85 113.26C374.43 79.32 338 55 295.08 55C288.78 55 282.63 55.53 276.66 56.53C262.19 23.02 225.83 0 183.72 0C129.52 0 85.75 39.43 85.75 88.37C85.75 96.99 87.15 105.31 89.77 113.19C40.04 116.48 0 153.75 0 199.63C0 250.57 43.92 290 98.12 290C99.71 290 101.3 289.97 102.88 289.9V290H379V289.96Z" 111 + /> 112 + </g> 113 + </g> 114 + 115 + <g filter="url(#shadow)"> 116 + <rect 117 + x="96" 118 + y="106" 119 + width="1008" 120 + height="418" 121 + rx="42" 122 + fill="url(#card)" 123 + stroke="#ffffff" 124 + stroke-opacity="0.64" 125 + /> 126 + <rect 127 + x="139" 128 + y="151" 129 + width="340" 130 + height="255" 131 + rx="28" 132 + fill="#ffffff" 133 + fill-opacity="0.48" 134 + stroke="#ffffff" 135 + stroke-opacity="0.7" 136 + /> 137 + <rect x="169" y="181" width="92" height="92" rx="24" fill="#2a5aa8" /> 138 + <circle cx="215" cy="227" r="26" fill="#ffffff" fill-opacity="0.92" /> 139 + <rect 140 + x="287" 141 + y="183" 142 + width="146" 143 + height="16" 144 + rx="8" 145 + fill="#0e1428" 146 + fill-opacity="0.72" 147 + /> 148 + <rect 149 + x="287" 150 + y="215" 151 + width="112" 152 + height="12" 153 + rx="6" 154 + fill="#0e1428" 155 + fill-opacity="0.34" 156 + /> 157 + <rect 158 + x="169" 159 + y="310" 160 + width="260" 161 + height="13" 162 + rx="7" 163 + fill="#0e1428" 164 + fill-opacity="0.32" 165 + /> 166 + <rect 167 + x="169" 168 + y="340" 169 + width="215" 170 + height="13" 171 + rx="7" 172 + fill="#0e1428" 173 + fill-opacity="0.24" 174 + /> 175 + <rect 176 + x="169" 177 + y="377" 178 + width="78" 179 + height="28" 180 + rx="14" 181 + fill="#2a5aa8" 182 + fill-opacity="0.15" 183 + /> 184 + <rect 185 + x="260" 186 + y="377" 187 + width="122" 188 + height="28" 189 + rx="14" 190 + fill="#2a5aa8" 191 + fill-opacity="0.15" 192 + /> 193 + 194 + <rect 195 + x="386" 196 + y="247" 197 + width="340" 198 + height="255" 199 + rx="28" 200 + fill="#ffffff" 201 + fill-opacity="0.38" 202 + stroke="#ffffff" 203 + stroke-opacity="0.58" 204 + /> 205 + <rect x="416" y="278" width="92" height="92" rx="24" fill="#d89a23" /> 206 + <circle cx="462" cy="324" r="25" fill="#ffffff" fill-opacity="0.9" /> 207 + <rect 208 + x="534" 209 + y="280" 210 + width="145" 211 + height="16" 212 + rx="8" 213 + fill="#0e1428" 214 + fill-opacity="0.64" 215 + /> 216 + <rect 217 + x="534" 218 + y="312" 219 + width="116" 220 + height="12" 221 + rx="6" 222 + fill="#0e1428" 223 + fill-opacity="0.28" 224 + /> 225 + <rect 226 + x="416" 227 + y="405" 228 + width="260" 229 + height="13" 230 + rx="7" 231 + fill="#0e1428" 232 + fill-opacity="0.28" 233 + /> 234 + <rect 235 + x="416" 236 + y="435" 237 + width="210" 238 + height="13" 239 + rx="7" 240 + fill="#0e1428" 241 + fill-opacity="0.22" 242 + /> 243 + 244 + <rect 245 + x="632" 246 + y="151" 247 + width="340" 248 + height="255" 249 + rx="28" 250 + fill="#ffffff" 251 + fill-opacity="0.5" 252 + stroke="#ffffff" 253 + stroke-opacity="0.72" 254 + /> 255 + <rect x="662" y="181" width="92" height="92" rx="24" fill="#6b7fd7" /> 256 + <circle cx="708" cy="227" r="26" fill="#ffffff" fill-opacity="0.92" /> 257 + <rect 258 + x="780" 259 + y="183" 260 + width="146" 261 + height="16" 262 + rx="8" 263 + fill="#0e1428" 264 + fill-opacity="0.72" 265 + /> 266 + <rect 267 + x="780" 268 + y="215" 269 + width="110" 270 + height="12" 271 + rx="6" 272 + fill="#0e1428" 273 + fill-opacity="0.34" 274 + /> 275 + <rect 276 + x="662" 277 + y="310" 278 + width="260" 279 + height="13" 280 + rx="7" 281 + fill="#0e1428" 282 + fill-opacity="0.32" 283 + /> 284 + <rect 285 + x="662" 286 + y="340" 287 + width="205" 288 + height="13" 289 + rx="7" 290 + fill="#0e1428" 291 + fill-opacity="0.24" 292 + /> 293 + <rect 294 + x="662" 295 + y="377" 296 + width="95" 297 + height="28" 298 + rx="14" 299 + fill="#2a5aa8" 300 + fill-opacity="0.15" 301 + /> 302 + </g> 303 + 304 + <text 305 + x="600" 306 + y="286" 307 + text-anchor="middle" 308 + fill="rgba(18,26,47,0.72)" 309 + font-family="ui-monospace, 'Cascadia Code', 'SF Mono', Menlo, Consolas, monospace" 310 + font-size="22" 311 + font-weight="700" 312 + letter-spacing="0.38em" 313 + >EXPLORE THE ATMOSPHERE</text> 314 + <text 315 + x="600" 316 + y="350" 317 + text-anchor="middle" 318 + fill="#0e1428" 319 + font-family="ui-monospace, 'Cascadia Code', 'SF Mono', Menlo, Consolas, monospace" 320 + font-size="52" 321 + font-weight="650" 322 + letter-spacing="-0.035em" 323 + >Apps, profiles, reviews</text> 324 + <text 325 + x="600" 326 + y="410" 327 + text-anchor="middle" 328 + fill="#0e1428" 329 + font-family="ui-monospace, 'Cascadia Code', 'SF Mono', Menlo, Consolas, monospace" 330 + font-size="52" 331 + font-weight="650" 332 + letter-spacing="-0.035em" 333 + >and updates.</text> 334 + <text 335 + x="600" 336 + y="468" 337 + text-anchor="middle" 338 + fill="rgba(18,26,47,0.84)" 339 + font-family="ui-sans-serif, system-ui, -apple-system, Segoe UI, sans-serif" 340 + font-size="25" 341 + font-weight="500" 342 + >A registry for projects building across the ATmosphere.</text> 343 + </svg>