The deploy pipeline does not pick up newly added /public files, so the svgl brand logos and the Backed by Y Combinator badge 404'd in production while the page HTML and _astro chunks served fine. Move the SVGs into src/assets and import them through Vite: the small logos inline as data URIs in the HTML/JS, and the larger badge emits as a hashed /_astro asset. Both serve.
Unified · Split
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
| @@ -5,6 +5,15 @@ import React, { forwardRef, useRef } from "react"; · 4 unmodified lines | |||
| 5 | import { cn } from "../lib/utils"; | 5 | import { cn } from "../lib/utils"; |
| 6 | import { AnimatedBeam } from "./ui/animated-beam"; | 6 | import { AnimatedBeam } from "./ui/animated-beam"; |
| 7 | 7 | ||
| 8 | // Imported so Vite emits them as hashed build assets (served from /_astro/...); | ||
| 9 | // the deploy pipeline does not pick up newly added /public files. | ||
| 10 | import sentryLogo from "../assets/logos/sentry.svg?url"; | ||
| 11 | import githubLogo from "../assets/logos/github.svg?url"; | ||
| 12 | import linearLogo from "../assets/logos/linear.svg?url"; | ||
| 13 | import claudeLogo from "../assets/logos/claude.svg?url"; | ||
| 14 | import cursorLogo from "../assets/logos/cursor.svg?url"; | ||
| 15 | import codexLogo from "../assets/logos/codex.svg?url"; | ||
| 16 | |||
| 8 | type Variant = "blueprint" | "brutalist" | "pastel" | "cyber" | "editorial" | "stripe"; | 17 | type Variant = "blueprint" | "brutalist" | "pastel" | "cyber" | "editorial" | "stripe"; |
| 9 | 18 | ||
| 10 | const variantStyles: Record< | 19 | const variantStyles: Record< |
| @@ -342,10 +351,10 @@ const logoIcon = (src: string) => () => ( · 331 unmodified lines | |||
| 342 | ); | 351 | ); |
| 343 | 352 | ||
| 344 | const Icons = { | 353 | const Icons = { |
| 345 | sentry: logoIcon("/logos/sentry.svg"), | 354 | sentry: logoIcon(sentryLogo), |
| 346 | github: logoIcon("/logos/github.svg"), | 355 | github: logoIcon(githubLogo), |
| 347 | linear: logoIcon("/logos/linear.svg"), | 356 | linear: logoIcon(linearLogo), |
| 348 | claude: logoIcon("/logos/claude.svg"), | 357 | claude: logoIcon(claudeLogo), |
| 349 | cursor: logoIcon("/logos/cursor.svg"), | 358 | cursor: logoIcon(cursorLogo), |
| 350 | codex: logoIcon("/logos/codex.svg"), | 359 | codex: logoIcon(codexLogo), |
| 351 | }; | 360 | }; |
| @@ -6,6 +6,29 @@ · 5 unmodified lines | |||
| 6 | 6 | ||
| 7 | import React, { useEffect, useRef, useState } from "react"; | 7 | import React, { useEffect, useRef, useState } from "react"; |
| 8 | 8 | ||
| 9 | // Brand logos are imported so Vite emits them as hashed build assets (served | ||
| 10 | // from /_astro/...) instead of relying on /public, which the deploy pipeline | ||
| 11 | // does not pick up for newly added files. | ||
| 12 | import githubLogo from "../assets/logos/github.svg?url"; | ||
| 13 | import stripeLogo from "../assets/logos/stripe.svg?url"; | ||
| 14 | import jiraLogo from "../assets/logos/jira.svg?url"; | ||
| 15 | import sentryLogo from "../assets/logos/sentry.svg?url"; | ||
| 16 | import linearLogo from "../assets/logos/linear.svg?url"; | ||
| 17 | import gmailLogo from "../assets/logos/gmail.svg?url"; | ||
| 18 | import notionLogo from "../assets/logos/notion.svg?url"; | ||
| 19 | import slackLogo from "../assets/logos/slack.svg?url"; | ||
| 20 | |||
| 21 | const LOGOS: Record<string, string> = { | ||
| 22 | github: githubLogo, | ||
| 23 | stripe: stripeLogo, | ||
| 24 | jira: jiraLogo, | ||
| 25 | sentry: sentryLogo, | ||
| 26 | linear: linearLogo, | ||
| 27 | gmail: gmailLogo, | ||
| 28 | notion: notionLogo, | ||
| 29 | slack: slackLogo, | ||
| 30 | }; | ||
| 31 | |||
| 9 | /** | 32 | /** |
| 10 | * Interactive "no context bloat" demo, laid out like Effect's "Production-grade | 33 | * Interactive "no context bloat" demo, laid out like Effect's "Production-grade |
| 11 | * TypeScript" section: a complexity gauge + service checklist on top, then two | 34 | * TypeScript" section: a complexity gauge + service checklist on top, then two |
| @@ -216,19 +239,13 @@ function useAnimatedNumber(target: number): number { · 204 unmodified lines | |||
| 216 | return display; | 239 | return display; |
| 217 | } | 240 | } |
| 218 | 241 | ||
| 219 | // Brand logos live in /public/logos as full-color svgl.app assets, rendered as | 242 | // Full-color svgl.app brand marks, rendered as <img> so each keeps its own |
| 220 | // <img> so each keeps its own colors. svgl has no Jira icon, so /logos/jira.svg | 243 | // colors. svgl has no Jira icon, so jira.svg is the Jira mark in Jira blue. |
| 221 | // is the Jira mark in Jira blue. | ||
| 222 | function IntegrationIcon({ slug }: { readonly slug: string }) { | 244 | function IntegrationIcon({ slug }: { readonly slug: string }) { |
| 245 | const src = LOGOS[slug]; | ||
| 246 | if (!src) return null; | ||
| 223 | return ( | 247 | return ( |
| 224 | <img | 248 | <img src={src} alt="" width={15} height={15} loading="lazy" style={{ objectFit: "contain" }} /> |
| 225 | src={`/logos/${slug}.svg`} | ||
| 226 | alt="" | ||
| 227 | width={15} | ||
| 228 | height={15} | ||
| 229 | loading="lazy" | ||
| 230 | style={{ objectFit: "contain" }} | ||
| 231 | /> | ||
| 232 | ); | 249 | ); |
| 233 | } | 250 | } |
| 234 | 251 | ||
| @@ -3,6 +3,9 @@ import Layout from "../layouts/Layout.astro"; · 2 unmodified lines | |||
| 3 | import { AnimatedBeamDemo } from "../components/animated-beam-demo"; | 3 | import { AnimatedBeamDemo } from "../components/animated-beam-demo"; |
| 4 | import { ContextBloatDemo } from "../components/context-bloat-demo"; | 4 | import { ContextBloatDemo } from "../components/context-bloat-demo"; |
| 5 | import { SelfHostContactModal } from "../components/self-host-contact-modal"; | 5 | import { SelfHostContactModal } from "../components/self-host-contact-modal"; |
| 6 | // Imported so Vite emits it as a hashed build asset (served from /_astro/...); | ||
| 7 | // the deploy pipeline does not pick up newly added /public files. | ||
| 8 | import ycBackedBy from "../assets/yc-backed-by.svg?url"; | ||
| 6 | 9 | ||
| 7 | const GH = "https://github.com/RhysSullivan/executor"; | 10 | const GH = "https://github.com/RhysSullivan/executor"; |
| 8 | 11 | ||
| @@ -162,7 +165,7 @@ Source (and the place to start if something breaks): https://github.com/RhysSull · 153 unmodified lines | |||
| 162 | aria-label="Backed by Y Combinator" | 165 | aria-label="Backed by Y Combinator" |
| 163 | > | 166 | > |
| 164 | <img | 167 | <img |
| 165 | src="/yc-backed-by.svg" | 168 | src={ycBackedBy} |
| 166 | alt="Backed by Y Combinator" | 169 | alt="Backed by Y Combinator" |
| 167 | width="140" | 170 | width="140" |
| 168 | height="52" | 171 | height="52" |