Constructs branded fallback cover-image URLs for entity cards (onboarding guides, blog posts, releases, etc.) that have no image, centralizing all URL-building logic so consumers never concatenate og-placeholder URLs themselves. ## Key Components ### Interfaces - **`OgPlaceholderEndpoints`** — Slice of `ChatRuntime.endpoints` needed for base URL resolution; accepts `ogPlaceholderUrl` (explicit) or `imageProxyUrlPrefix` (derived fallback). - **`BuildOgPlaceholderOptions`** — Options for `site` label, `aspect` ratio (`'wide'` | `'square'`), and explicit `width`/`height` pixel overrides. ### Functions - **`resolveOgPlaceholderBase(endpoints?)`** *(internal)* — Resolves the route base via three-tier priority: explicit `ogPlaceholderUrl` → derived from `imageProxyUrlPrefix` (swaps `/image-proxy` for `/og-placeholder`) → same-origin relative `/api/og-placeholder`. - **`buildOgPlaceholderUrl(endpoints, title, options?)`** — Primary entry point. Resolves the base URL, preserves any pre-existing query params, and appends `title`, `site`, and dimension params. SSR- and browser-safe; always returns a usable URL. ## Usage Example ```typescript import { buildOgPlaceholderUrl } from './og-placeholder' const endpoints = { imageProxyUrlPrefix: 'https://api.example.com/image-proxy', } // Wide fallback (1200×630, route default) const wideUrl = buildOgPlaceholderUrl(endpoints, 'Getting Started Guide', { site: 'Docs', aspect: 'wide', }) // → https://api.example.com/og-placeholder?title=Getting+Started+Guide&site=Docs // Square fallback for compact chat-inline slots (1024×1024) const squareUrl = buildOgPlaceholderUrl(endpoints, 'Release v2.0', { aspect: 'square', }) // → https://api.example.com/og-placeholder?title=Release+v2.0&w=1024&h=1024 // Explicit dimensions win over aspect const customUrl = buildOgPlaceholderUrl(endpoints, 'Case Study', { width: 800, height: 400, }) ``` **Base resolution priority:** `ogPlaceholderUrl` → derived from `imageProxyUrlPrefix` → `/api/og-placeholder` (same-origin fallback). Per-platform brand colors are resolved server-side — never baked into the URL.