Skip to content

Add social preview tags for home-page deep links (?q= / ?prompt=)#2356

Open
gary149 wants to merge 1 commit into
mainfrom
claude/modest-brahmagupta-m84euk
Open

Add social preview tags for home-page deep links (?q= / ?prompt=)#2356
gary149 wants to merge 1 commit into
mainfrom
claude/modest-brahmagupta-m84euk

Conversation

@gary149

@gary149 gary149 commented Jun 14, 2026

Copy link
Copy Markdown
Collaborator

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.png endpoint, 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 renders PromptPreviewTags when 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

  • The prompt text is fully attacker-controllable via the query string, but the render pipeline treats it as inert text: it's sanitized and length-capped by 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.
  • Distinct prompts are bounded by an LRU cache shared with the existing share endpoint.
  • Empty or non-renderable prompts fall back to the generic app card.

https://claude.ai/code/session_016eCeQ68BbXXSbyBTukpSqD

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

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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".

Comment on lines +23 to +24
const png = await getShareThumbnailPng({
prompt,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 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 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants