Skip to content

feat(desktop): render links as rich previews - #3770

Open
tellaho wants to merge 21 commits into
mainfrom
tho/link-preview-images
Open

feat(desktop): render links as rich previews#3770
tellaho wants to merge 21 commits into
mainfrom
tho/link-preview-images

Conversation

@tellaho

@tellaho tellaho commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

Category: improvement
User Impact: Links render as compact horizontal previews with page context, safe imagery, and clear site identity without taking over the conversation.

Problem: Plain links and the previous preview treatment did not provide enough visual context. Preview identity, imagery, loading geometry, and author controls were also inconsistent.

Solution: Add compact horizontal link previews with a responsive thumbnail, normalized hostname, favicon, title, and description. The preview reserves its media geometry while metadata resolves to avoid shifting the reading position, then collapses to the compact no-image treatment when no image is available. Authors can remove previews with concise consequence-focused confirmation copy.

The metadata pipeline safely discovers and sanitizes page imagery and favicons while enforcing SSRF, type, size, animation, and dimension limits. The same preview data is preserved across message, inbox, feed, and forum surfaces.

File changes
  • Adds bounded metadata and media discovery with safe favicon and preview-image sanitization.
  • Carries resolved preview metadata through native events, message delivery, and forum messages.
  • Preserves preview attachments while formatting timelines and rendering message surfaces.
  • Renders compact horizontal cards with responsive, geometry-stable thumbnails and normalized site identity.
  • Adds deterministic fixtures and coverage for metadata resolution, image/no-image transitions, removal, and responsive layout.
  • Extracts native command input and preview helpers to satisfy lint and file-size boundaries without suppressions.

Reproduction Steps

  1. Open the desktop composer and paste a standard webpage, a GitHub page, and a tweet.
  2. Confirm each resolves to a compact horizontal preview with normalized hostname, title/description, favicon, and a thumbnail when available.
  3. Confirm the card does not change height or text wrapping while an image resolves; a no-image result collapses to the compact identity treatment.
  4. Resize to a narrow viewport and confirm the thumbnail becomes smaller while retaining the horizontal layout.
  5. As the message author, remove the preview and confirm the concise removal dialog appears.
  6. Send the message and confirm the same preview treatment appears in timelines, inbox, feed, and forum surfaces.

Validation

  • Desktop unit suite: 3,857/3,857
  • Compact preview Playwright coverage: 3/3
  • TypeScript, Biome, E2E build, Rust/Tauri checks, and push guards passed
  • CI green at 502f8fdda3a796e323b22fc4c0100c7d7a608138

Screenshots / Demos

Captured against the real public link https://github.com/block/goose, using its live OpenGraph title, description, image, and GitHub favicon after the production sanitization path.
image

Supersedes #3316 by combining metadata enrichment and the compact preview experience into one reviewable change.

@tellaho tellaho changed the title feat(desktop): unfurl safe link preview images feat(desktop): render links as rich previews Jul 30, 2026
@tellaho
tellaho changed the base branch from tho/link-ograph-embeds to main July 30, 2026 17:42
@tellaho
tellaho marked this pull request as ready for review July 30, 2026 18:01
@tellaho
tellaho requested a review from a team as a code owner July 30, 2026 18:01
npub1223z34hd7vtwc6qj4s7flsxkj644nlre2nthu7lrrmkumhu3xddsrx9r6w and others added 21 commits July 30, 2026 16:39
Co-authored-by: npub14vtk7pvazqrq9639qu7e560wnqtl0d53ca4gjuvq6jzf3k2el23qqlwa7f <ab176f059d100602ea25073d9a69ee9817f7b691c76a897180d48498d959faa2@buzz.block.builderlab.xyz>
Signed-off-by: npub14vtk7pvazqrq9639qu7e560wnqtl0d53ca4gjuvq6jzf3k2el23qqlwa7f <ab176f059d100602ea25073d9a69ee9817f7b691c76a897180d48498d959faa2@buzz.block.builderlab.xyz>
Co-authored-by: npub14vtk7pvazqrq9639qu7e560wnqtl0d53ca4gjuvq6jzf3k2el23qqlwa7f <ab176f059d100602ea25073d9a69ee9817f7b691c76a897180d48498d959faa2@buzz.block.builderlab.xyz>
Signed-off-by: npub14vtk7pvazqrq9639qu7e560wnqtl0d53ca4gjuvq6jzf3k2el23qqlwa7f <ab176f059d100602ea25073d9a69ee9817f7b691c76a897180d48498d959faa2@buzz.block.builderlab.xyz>
Co-authored-by: Taylor Ho <taylorkmho@gmail.com>
Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
Co-authored-by: npub14vtk7pvazqrq9639qu7e560wnqtl0d53ca4gjuvq6jzf3k2el23qqlwa7f <ab176f059d100602ea25073d9a69ee9817f7b691c76a897180d48498d959faa2@buzz.block.builderlab.xyz>
Signed-off-by: npub14vtk7pvazqrq9639qu7e560wnqtl0d53ca4gjuvq6jzf3k2el23qqlwa7f <ab176f059d100602ea25073d9a69ee9817f7b691c76a897180d48498d959faa2@buzz.block.builderlab.xyz>
Co-authored-by: npub14vtk7pvazqrq9639qu7e560wnqtl0d53ca4gjuvq6jzf3k2el23qqlwa7f <ab176f059d100602ea25073d9a69ee9817f7b691c76a897180d48498d959faa2@buzz.block.builderlab.xyz>
Signed-off-by: npub14vtk7pvazqrq9639qu7e560wnqtl0d53ca4gjuvq6jzf3k2el23qqlwa7f <ab176f059d100602ea25073d9a69ee9817f7b691c76a897180d48498d959faa2@buzz.block.builderlab.xyz>
Co-authored-by: npub14vtk7pvazqrq9639qu7e560wnqtl0d53ca4gjuvq6jzf3k2el23qqlwa7f <ab176f059d100602ea25073d9a69ee9817f7b691c76a897180d48498d959faa2@buzz.block.builderlab.xyz>
Signed-off-by: npub14vtk7pvazqrq9639qu7e560wnqtl0d53ca4gjuvq6jzf3k2el23qqlwa7f <ab176f059d100602ea25073d9a69ee9817f7b691c76a897180d48498d959faa2@buzz.block.builderlab.xyz>
…eview card

- link-preview-attachment.tsx: add optional onRemove prop that renders a
  small X button floating just outside the card's top-right corner
  (left-full top-0, z-20 above the link trigger), fading in on message
  row hover via group-hover/message and reachable by keyboard via
  focus-visible
- link-preview-attachment.tsx: wrap the card in a relative container
  carrying the width classes (w-80 max-w-full shrink-0) since the card's
  overflow-hidden would clip a button positioned outside its bounds
- link-preview-attachment.tsx: remove the AttachmentActions block with
  the hover-revealed ExternalLink icon, eliminating the reserved flex
  gap on card hover
- markdown.tsx: drop the "Remove previews for everyone" text button
  below the preview list and its spacing wrapper; pass onRemove to each
  LinkPreviewAttachment instead, opening the same confirmation dialog
- removal flow and AlertDialog confirmation are unchanged; X only
  renders when onRemoveLinkPreviewsForEveryone is available (own
  messages)

Co-authored-by: Taylor Ho <taylorkmho@gmail.com>
Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
…cards

- Remove the "Automatic preview from {domain}" gradient overlay on preview
  images; simplify the img alt text to "Preview from {domain}"
- Drop the "· {typeLabel}" suffix (PR/issue/repo/file/link/etc.) from the
  card metadata row — the provider name and logo already convey the source,
  so the row now shows just the provider
- typeLabel stays in the SupportedLinkPreview model since it still powers
  the accessible aria-label ("Open GitHub PR: ...")

Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
Co-authored-by: Taylor Ho <taylorkmho@gmail.com>
Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
Co-authored-by: Taylor Ho <taylorkmho@gmail.com>
Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
Co-authored-by: Taylor Ho <taylorkmho@gmail.com>
Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
Co-authored-by: Taylor Ho <taylorkmho@gmail.com>
Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
Co-authored-by: Taylor Ho <taylorkmho@gmail.com>
Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
Co-authored-by: Taylor Ho <taylorkmho@gmail.com>
Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
Co-authored-by: Taylor Ho <taylorkmho@gmail.com>
Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
Co-authored-by: Taylor Ho <taylorkmho@gmail.com>
Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
Co-authored-by: Taylor Ho <taylorkmho@gmail.com>
Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
Co-authored-by: Taylor Ho <taylorkmho@gmail.com>
Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
Co-authored-by: Taylor Ho <taylorkmho@gmail.com>
Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
Co-authored-by: Taylor Ho <taylorkmho@gmail.com>
Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
Co-authored-by: Taylor Ho <taylorkmho@gmail.com>
Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
Co-authored-by: Taylor Ho <taylorkmho@gmail.com>
Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
@tellaho
tellaho force-pushed the tho/link-preview-images branch from 502f8fd to 3990447 Compare July 31, 2026 00:14
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.

1 participant