Skip to content

feat(desktop): adding rich link previews to messages - #3818

Open
tellaho wants to merge 1 commit into
mainfrom
tho/link-preview-rich-setting
Open

feat(desktop): adding rich link previews to messages#3818
tellaho wants to merge 1 commit into
mainfrom
tho/link-preview-rich-setting

Conversation

@tellaho

@tellaho tellaho commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

Overview

Category: improvement
User impact: Link previews appear in the composer and travel as privacy-safe sender-authored snapshots, so recipients never contact the linked site merely by opening a conversation.
Problem: Cold-cache link paste could freeze the composer before the URL painted; recipient-side unfurling leaked visits; invalid or unresolved preview work could interfere with sending or leave dead cards behind.
Solution: Paint pasted links before starting cold resolver work, resolve only in the sender's composer, attach only complete validated snapshots at Send, and render authored snapshots without recipient fallback fetching.

Behavior

  • Cold paste stays responsive: bare and angle-bracket URL paste paths commit the visible link before resolver work begins.
  • Sender-only fetching: metadata is resolved while composing; recipients render only the sender-authored snapshot.
  • Send never waits: pending, failed, invalid, and unsendable previews are omitted. They do not block or cancel the message.
  • Terminal misses disappear: failed, timed-out, or 404 resolver results remove the composer card while preserving visible link text.
  • Display-text links work: Markdown links such as [review the pull request](…) produce and send the same snapshots as bare URLs.
  • Compact and Rich presentation: Compact remains the default; Rich preserves source description line breaks and paragraphs.
  • Immediate draft-wide dismissal: clicking × immediately hides all previews for the draft, suppresses links pasted later, and emits only ["link-preview", "none"]. No confirmation detour. Suppression resets after send or clearing the draft.
  • Zero recipient fallback: missing, stale, malformed, off-relay, unsupported, or suppressed snapshots remain ordinary visible links; recipients never regenerate them.

Implementation

  • Resolve previews from deferred composer URL state so paste can paint first.
  • Upload finished preview media to the active community relay and snapshot only valid, sendable media references.
  • Atomically capture ready snapshots at submit time; never append a late preview after send.
  • Validate snapshot and suppression tags in desktop/native and relay ingestion, rejecting duplicate or mixed forms.
  • Render composer previews as stable 55px attachment cards at desktop and narrow widths.
  • Add deterministic E2E coverage for cold paste, ready/pending/failed/invalid previews, display-text links, multiline Rich descriptions, immediate dismissal, later-pasted links, and suppression reset.

Validation

Validated head: 9807ba8952f190e76153834abf8ab61dd40be5e2

  • Push hooks passed: check-push-org, branch skew, desktop check, mobile tests, desktop tests, Rust tests, and desktop Tauri checks.
  • Focused screenshot E2E at the validated head: 5/5 passed across Compact/Rich composer and recipient states, 800px/420px geometry, display-text links, multiline descriptions, and immediate dismissal.
  • PR CI was triggered for this exact head and is currently running; completed checks are green at the time of this update.
  • Worktree is clean and both PR head and validated branch resolve to 9807ba895….

Screenshots

Compact composer

Loading Ready
Compact composer loading Compact composer ready

Rich composer

Loading Ready
Rich composer loading Rich composer ready

Responsive composer

800px loading 800px ready
800px composer loading 800px composer ready
420px loading 420px ready
420px composer loading 420px composer ready

Recipient presentation

Compact Rich
Recipient compact Recipient rich

Display-text Markdown link

Composer Recipient
Display-text link in composer Display-text link with recipient preview

Rich multiline description

Rich preview preserving description paragraphs

Immediate dismissal

Before × Immediately after ×
Preview before immediate dismissal Composer immediately after preview dismissal

@tellaho
tellaho force-pushed the tho/link-preview-rich-setting branch 2 times, most recently from 76a5892 to 85ef69d Compare July 30, 2026 23:22
@tellaho
tellaho marked this pull request as ready for review July 30, 2026 23:37
@tellaho
tellaho requested a review from a team as a code owner July 30, 2026 23:37
@tellaho
tellaho force-pushed the tho/link-preview-images branch from 502f8fd to 3990447 Compare July 31, 2026 00:14
@tellaho
tellaho force-pushed the tho/link-preview-rich-setting branch from 85ef69d to a2ffdf8 Compare July 31, 2026 06:42
@tellaho tellaho changed the title feat(desktop): add rich link preview preference feat(desktop): render links as rich previews Jul 31, 2026
@tellaho
tellaho changed the base branch from tho/link-preview-images to main July 31, 2026 06:43
@Chessing234

Copy link
Copy Markdown
Contributor

rich previews are nice. make sure they stay opt-in or gated for private/untrusted hosts so we dont fetch surprise content

@wesbillman wesbillman left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Reviewing on Wes Billman's behalf. Requesting changes for a recipient privacy blocker at 60d348e526b96001acf6e57479b2fed3c6289245:

[P1 privacy] Merely viewing an unsolicited message automatically contacts sender-controlled hosts from the recipient's machine, with no off or consent control. Interactive unsuppressed messages extract preview URLs (desktop/src/shared/ui/markdown.tsx:1857-1863), the hook immediately loads each URL (desktop/src/shared/lib/useResolvedLinkPreviews.ts:223-277), and native code performs a direct GET with a Buzz Desktop user-agent (desktop/src-tauri/src/commands/link_preview.rs:191-215). The only preference is compact | rich, defaulting to compact—not off (desktop/src/shared/lib/linkPreviewStylePreference.ts:3-18). This exposes the recipient's IP and request timing to a message author without recipient action.

The SSRF hardening is strong, but it protects the machine rather than the user's network privacy. Please use a safe proxy, default-off/explicit enablement, consent before fetching, or another design that prevents unsolicited recipient-side requests. Please also add negative tests around the preview-specific URL gate (HTTP, credentials, non-443 ports, and private-IP hosts).

@tellaho
tellaho marked this pull request as draft August 3, 2026 21:42
@tellaho tellaho changed the title feat(desktop): render links as rich previews feat(desktop): send authored link preview snapshots Aug 3, 2026
@tellaho
tellaho force-pushed the tho/link-preview-rich-setting branch 3 times, most recently from 99920f0 to db07f35 Compare August 4, 2026 18:39
@tellaho

tellaho commented Aug 5, 2026

Copy link
Copy Markdown
Contributor Author

@wesbillman — commenting on Taylor Ho's behalf:

Thanks for flagging this. We revised the design so viewing a message never contacts the linked origin from the recipient’s machine.

Link previews are now resolved by the sender while composing and attached to the message as validated snapshots. Recipients render only those snapshots, with no recipient-side unfurl fallback. If a snapshot is missing, invalid, unfinished, or intentionally suppressed, the link remains ordinary clickable text and no origin request occurs.

This preserves link previews while removing the unsolicited recipient network request identified in your review. We also added coverage for the no-fetch recipient paths and retained the URL-safety checks on sender-side resolution.

Sender preview in composer Authored snapshot rendered for recipient
Sender preview ready in composer Authored compact snapshot rendered for recipient
Rich authored snapshot Preview suppression leaves ordinary link text
Rich authored snapshot rendered for recipient Composer after immediate preview suppression

@tellaho
tellaho marked this pull request as ready for review August 5, 2026 18:38
@tellaho tellaho changed the title feat(desktop): send authored link preview snapshots feat(desktop): adding rich link previews to messages Aug 5, 2026

@wesbillman wesbillman left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Reviewing on Wes Billman’s behalf.

The recipient privacy blocker is resolved: recipient rendering now consumes only validated sender-authored snapshots and does not invoke the metadata resolver.

[P1] Multiline preview descriptions make the relay reject the entire message. The sender intentionally preserves \n in snapshot descriptions (desktop/src/shared/lib/linkPreviewSnapshot.ts:41-55,161-178), native send validation explicitly permits those newlines (desktop/src-tauri/src/link_preview_tags.rs:9-14,153-158), and the composer attaches that description to the outgoing snapshot (desktop/src/features/messages/ui/useComposerLinkPreviews.tsx:185-204). Relay ingest then rejects every control character—including \n—for all three text fields (crates/buzz-relay/src/handlers/ingest.rs:206-213). This is not merely a missing preview: because the snapshot tag is present, the relay rejects the message send. The PR’s own multiline Rich behavior therefore cannot pass against the real relay despite the bridge E2E succeeding.

Please align relay validation with the native/TypeScript contract by allowing newline only for the description field (while continuing to reject other controls), and add relay coverage for accepted description newlines plus rejected title/site newlines and non-newline controls. The dungeon has achieved cross-layer disagreement, its traditional favorite.

@tellaho

tellaho commented Aug 5, 2026

Copy link
Copy Markdown
Contributor Author

@wesbillman — commenting on Taylor Ho's behalf:

Addressed the cross-layer validation mismatch at 69a747bf60d065a787471230c582ce9b9d296c38. Relay ingest now permits newlines only in snapshot descriptions, matching desktop/native validation, while rejecting title/site newlines and every other control character.

Added relay coverage for the accepted multiline-description case and each requested rejection case. The focused relay tests (3/3) and the full pre-push suite pass on the pushed head.

@wesbillman wesbillman left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Re-reviewed on Wes Billman’s behalf at 69a747bf60d065a787471230c582ce9b9d296c38. The requested relay-validation fix is correct; no remaining blocking findings in this update.

Relay ingest now permits \n only in preview descriptions, while title/site newlines and all other control characters remain rejected. The added focused relay tests cover the accepted multiline-description contract and each requested rejection class, bringing relay behavior into agreement with native and TypeScript validation.

Several CI jobs were still running when I submitted this review; completed jobs were green.

wesbillman
wesbillman previously approved these changes Aug 5, 2026
Co-authored-by: Taylor Ho <taylorkmho@gmail.com>

Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
@tellaho
tellaho force-pushed the tho/link-preview-rich-setting branch from 5c2ba22 to 33b754a Compare August 6, 2026 01:57
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.

3 participants