Add social preview tags for home-page deep links (?q= / ?prompt=)#2356
Add social preview tags for home-page deep links (?q= / ?prompt=)#2356gary149 wants to merge 1 commit into
Conversation
Sharing a home-page deep link (e.g. /?q=... or /?prompt=...) now unfurls with the same prompt card that shared conversations get, instead of the generic app thumbnail. - GET /thumbnail.png?q=/?prompt= renders the 1200x648 card via the existing satori+resvg pipeline (getShareThumbnailPng), reusing the same sanitizer, generic-card fallback, LRU cache, and long cache headers. - The home page server-renders prompt-specific og:/twitter: tags (PromptPreviewTags.svelte) so crawlers — which never run the onMount that consumes these params — see the right card; the layout suppresses its generic tags for these links so only one og:image is emitted. - promptFromLinkParams() is the single source of truth for the prompt carried by a link (q wins over prompt, matching the home page), shared by the page, layout, and thumbnail endpoint. - handle.ts lets /thumbnail.png through the auth gate, like the existing model/share thumbnail endpoints. Query text is fully attacker-controlled but rendered as inert text (no HTML parsing, no network, sanitized + length-capped), and tags are escaped/encoded. https://claude.ai/code/session_016eCeQ68BbXXSbyBTukpSqD
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 822f01a3b5
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| const png = await getShareThumbnailPng({ | ||
| prompt, |
There was a problem hiding this comment.
Fall back to the app thumbnail for unusable prompts
When ?q/?prompt contains only text the thumbnail renderer drops (for example emoji-only or pure CJK text), renderableThumbnailText(...) returns "", but this still calls getShareThumbnailPng; the shared-thumbnail renderer treats an empty prompt as the shared-conversation fallback ("A conversation shared with you" in src/lib/server/shareThumbnail/shareThumbnail.ts:72). Since the layout suppresses the generic app tags for these deep links, the unfurled image becomes a misleading conversation-share card instead of the app preview; return/redirect to the generic app thumbnail when prompt is empty.
Useful? React with 👍 / 👎.
Summary
Adds support for generating social-preview cards (Open Graph / Twitter meta tags) when sharing home-page deep links with
?q=or?prompt=query parameters. This allows shared links to unfurl with the prompt text instead of the generic app card.Key Changes
New utility function
promptFromLinkParams()(src/lib/utils/urlParams.ts): Extracts and sanitizes the prompt from?q=or?prompt=parameters, with?q=taking precedence. Includes comprehensive test coverage.New component
PromptPreviewTags.svelte: Renders Open Graph and Twitter meta tags dynamically based on the prompt text. Generates a social-preview thumbnail via the new/thumbnail.pngendpoint, with text capped at 240 characters for optimal card rendering.New endpoint
GET /thumbnail.png(src/routes/thumbnail.png/+server.ts): Generates PNG social-preview cards on-demand from query-string prompts. Reuses the existing share-thumbnail rendering pipeline with aggressive caching (24h public, 7d CDN) since the card is a pure function of the sanitized prompt.Updated home page (
src/routes/+page.svelte): Conditionally rendersPromptPreviewTagswhen a prompt deep link is detected, enabling server-side rendering of preview tags for link unfurlers.Updated layout (
src/routes/+layout.svelte): Suppresses generic meta tags when rendering a prompt-specific deep link to avoid tag duplication.Implementation Details
renderableThumbnailText(), the satori element tree is built from plain objects (never parsed as HTML), and no network calls happen at render time — making it as safe as rendering stored shares.https://claude.ai/code/session_016eCeQ68BbXXSbyBTukpSqD