Skip to content

feat(desktop): send authored link preview snapshots - #3818

Draft
tellaho wants to merge 43 commits into
mainfrom
tho/link-preview-rich-setting
Draft

feat(desktop): send authored link preview snapshots#3818
tellaho wants to merge 43 commits 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 attachment tray and travel as privacy-safe sender-authored snapshots, so opening a conversation never contacts the linked site.
Problem: Recipient-side unfurling leaked link visits, composer cards looked like timeline content, and preview work could interfere with sending.
Solution: Resolve previews only in the sender's composer, upload finished preview media to the active community relay, attach only complete versioned snapshots at Send, and render only strictly validated snapshots on recipients.

Privacy and delivery model

  • Sender-only fetching: origin metadata is resolved while composing. Compact attachment cards immediately show URL/domain identity—never “Preparing preview.”
  • Zero recipient fallback: received and optimistic messages render authored snapshot tags only. Missing, stale, malformed, off-relay, unsupported, or suppressed snapshots stay ordinary visible links; recipients never regenerate them.
  • Send never waits: submit atomically captures snapshots already ready at that moment. Pending or failed previews are gracefully omitted and are never added after send.
  • Draft-wide suppression: one Hide previews X suppresses every preview in the current draft, including links pasted afterward. It emits only ["link-preview", "none"], preserves clickable URL text, and resets after send or clearing the draft.
  • Durable but intentionally stale: snapshots preserve what the sender saw when sending; later origin changes do not silently rewrite message presentation.

Changes

  • Render composer previews as stable 55px attachment cards at desktop and narrow widths.
  • Show hostname/path/type while loading, then title/provider with local image or favicon fallback.
  • Add one draft-wide Hide previews control rather than per-link removal.
  • Carry ready snapshots or the exclusive suppression marker through native event construction.
  • Validate the suppression marker in desktop and relay ingestion, rejecting duplicates or mixed snapshot tags.
  • Keep recipient rendering authored-snapshot-only; plain links never trigger recipient origin fetching.
  • Add deterministic E2E coverage for ready snapshots, immediate send while pending, global suppression, later pasted links, and reset after send.

Validation

At 11b60d88f:

  • pnpm --dir desktop typecheck — passed.
  • pnpm --dir desktop exec biome check src/features/messages/ui/MessageComposer.tsx src/features/messages/ui/useComposerLinkPreviews.tsx tests/e2e/messaging.spec.ts — passed.
  • pnpm --dir desktop exec playwright test --project=smoke messaging.spec.ts — 42/42 passed.
  • cargo test --manifest-path desktop/src-tauri/Cargo.toml link_preview_tags — 3/3 passed.
  • cargo test -p buzz-relay link_preview_suppression — 3/3 passed.
  • Push hooks — desktop check/test, Rust tests, and desktop Tauri checks passed.
  • Real built-app screenshot capture — passed; desktop and narrow loading/ready states inspected below.

Screenshots

Desktop composer: loading Desktop composer: ready
Desktop composer loading Desktop composer ready
Narrow composer: loading Narrow composer: ready
Narrow composer loading Narrow composer ready
Recipient: Compact Recipient: Rich
Recipient compact Recipient rich

@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
npub1223z34hd7vtwc6qj4s7flsxkj644nlre2nthu7lrrmkumhu3xddsrx9r6w and others added 24 commits July 30, 2026 23:20
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>
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 added 9 commits July 30, 2026 23:21
- Replace separate description and image controls with one Show more/Show less toggle that collapses both elements.
- Expand rich preview text to the full available metadata and widen preview containers for readable 280-character tweets.
- Add vertical padding beside the accent border and cap preview images at 300px while preserving responsive sizing.

Co-authored-by: Taylor Ho <taylorkmho@gmail.com>
Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
- Update rich preview cards with wider layouts, compact image sizing, and unified content expansion controls
- Allow link titles to span two lines and preserve full expanded descriptions
- Raise fetched description metadata to the standard 280-character X post limit
- Preserve encoded description line breaks while normalizing intra-line whitespace
- Add metadata tests covering line breaks and description length limits

Co-authored-by: Taylor Ho <taylorkmho@gmail.com>
Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
- Render OG description line breaks instead of collapsing them into spaces
- Group blank-line-separated paragraphs with compact spacing to avoid oversized gaps
- Reuse the rich preview image component for consistent skeleton and lightbox behavior across link types

Co-authored-by: Taylor Ho <taylorkmho@gmail.com>
Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
- Route rich link preview images through the markdown gallery lightbox so preview images support consistent zoom transitions and gallery navigation.
- Extract link preview lightbox behavior and focusable-element discovery into focused markdown modules to keep the main renderer within its size limit.
- Infer image dimensions from loaded thumbnails when explicit lightbox metadata is unavailable.
- Pass the shared lightbox component through the link preview list and attachment layers.
- Render compact preview cards inline with wrapping and place the remove action on the final compact card.
- Preserve rich preview stacking and existing remove behavior for rich layouts.

Co-authored-by: Taylor Ho <taylorkmho@gmail.com>
Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
- Add a hover-only vertical ellipsis beside the first compact or rich preview.
- Provide a Link display submenu for switching the global Rich or Compact appearance preference.
- Show an actionable confirmation-backed Remove preview option for message authors while keeping display controls available to everyone.
- Link preference-change toasts to Appearance settings and expand rich preview images to match uploaded image width.

Co-authored-by: Taylor Ho <taylorkmho@gmail.com>
Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
- Remove compact card borders and background surfaces while preserving transparent hover behavior.
- Round thumbnail images on every corner and expand cards to the 24rem image-upload width.
- Shorten the preview settings submenu label from Link display to Display.
- Update compact preview geometry coverage for the wider borderless layout and await screenshot animations.

Co-authored-by: Taylor Ho <taylorkmho@gmail.com>
Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
- Expand compact preview thumbnails to an 88px height with slightly wider responsive dimensions.
- Reduce the thumbnail-to-content inset so preview text sits closer to its image.
- Allow compact titles to wrap across two lines for better readability.
- De-emphasize hostnames and descriptions with a softer secondary foreground color.
- Update desktop geometry coverage for the revised dimensions, spacing, and title treatment.

Co-authored-by: Taylor Ho <taylorkmho@gmail.com>
Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
- Remove legacy provider-specific compact preview logos and layouts in favor of hostname-driven metadata.
- Present imageless compact previews with the same 84px height, description support, and left-rule treatment as image-backed cards.
- Preserve fetched favicons and make favicon/hostname identity rows link directly to their source URL.
- Align rich and compact hostname and description colors on the same secondary foreground treatment.
- Expand messaging E2E coverage for equal-height cards, generic favicons, linked hostnames, and imageless descriptions.

Co-authored-by: Taylor Ho <taylorkmho@gmail.com>
Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
- Underline compact preview hostnames and titles together whenever the card is hovered.
- Remove independent hostname hover styling so the interaction consistently represents the full destination URL.
- Restore stronger description colors for standard rich previews and tweet content while keeping hostnames secondary.
- Add E2E coverage confirming both compact identity elements receive the shared hover affordance.

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 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

npub1223z34hd7vtwc6qj4s7flsxkj644nlre2nthu7lrrmkumhu3xddsrx9r6w and others added 5 commits July 31, 2026 09:24
Co-authored-by: Taylor Ho <taylorkmho@gmail.com>
Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
…-setting

* origin/main:
  feat(desktop): import local Pocket voices (#3259)
  fix(desktop): open profiles from avatars (#3751)
  refactor(voice): extract reusable Pocket primitives + Pocket voice settings (relands #2467 + #3208) (#3910)
  docs: add VISION_REMOTE_AGENTS.md (#3924)
  feat(desktop): auto-enable huddle transcription for agents (#3180)
  feat(agent): optional reply guard reminds a silent turn to publish (#3763)
  feat(desktop): upgrade Pocket TTS model (#3266)

Signed-off-by: npub1223z34hd7vtwc6qj4s7flsxkj644nlre2nthu7lrrmkumhu3xddsrx9r6w <52a228d6edf316ec6812ac3c9fc0d696ab59fc7954d77e7be31eedcddf91335b@buzz.block.builderlab.xyz>
Co-authored-by: Taylor Ho <taylorkmho@gmail.com>
Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
- Classify native preview image outcomes and honor bounded Retry-After cooldowns for rate-limited hosts
- Extract host cooldown bookkeeping to keep the preview command within the desktop file-size guard
- Fetch preview images and favicons independently so secondary failures preserve resolved metadata
- Add bounded frontend concurrency, fragment deduplication, expiring negative cache entries, and cooldown-aware retries
- Render deterministic Compact and Rich fallbacks for transient fetch and browser decode failures
- Expand Rust, metadata-loader, mock bridge, and Playwright coverage for throttling and recovery

Co-authored-by: Taylor Ho <taylorkmho@gmail.com>
Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
- Incorporate upstream desktop changes required by the pre-push overlap guard
- Preserve the link preview thumbnail stabilization work on the updated base

Co-authored-by: Taylor Ho <taylorkmho@gmail.com>

* origin/main: (26 commits)
  docs: formal spec for remote agents and their management (#3748)
  fix(nip-oa): accept raw Nostr tag form in parse_json_array (#4203)
  perf(relay): serve relay-membership checks from the read replica (#4124)
  chore(deps): bump nostr-relay-pool for RUSTSEC-2026-0224 (#4139)
  docs(nostr): document #h requirement for live reaction subscriptions (#3487)
  docs(chart): fix ArgoCD example for native OCI sources (full artifact repoURL + path) (#3426)
  docs(readme): clarify which release asset to download per platform (#3481)
  fix(relay): allow open relays to set their NIP-11 workspace icon (kind:9033) (#3998)
  docs: note that addressable channel events scope by d, not h (#4103)
  docs: fix stale kind count, quick-start numbering, and empty Further Reading (#2613)
  fix(desktop): keep thread-open affordance in archived channels (#4012)
  docs: add one-click Railway deploy for a hosted relay (#2733)
  fix(desktop): point Oh My Pi preset at omp.sh (#3516)
  fix(mesh): stop restarting a busy or loading shared-compute node (#3909)
  fix(desktop): preserve first huddle speech (#3962)
  feat(desktop): Agent Trading Cards — mintable agent-snapshot card PNGs with optional NIP-44 lock (#3278)
  fix(buzz-acp): thread cache-read tokens into NIP-AM kind:44200 events (#3999)
  feat(relay): accept kind:30621 multi-repo projects at ingest (#3171)
  fix(release): preserve main in desktop PR body (#3979)
  chore(release): release Buzz Desktop version 0.5.3 (#3972)
  ...

Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
- Import the retry-after bound only in the link preview test module
- Keep production Tauri builds free of the unused-import warning enforced by pre-push clippy

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

@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).

Co-authored-by: Taylor Ho <taylorkmho@gmail.com>
Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
@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
npub1223z34hd7vtwc6qj4s7flsxkj644nlre2nthu7lrrmkumhu3xddsrx9r6w and others added 4 commits August 3, 2026 15:06
Co-authored-by: Taylor Ho <taylorkmho@gmail.com>
Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
…er-snapshots

* origin/main: (29 commits)
  feat(agents): model-tuning parity in global Agent Defaults editor (#4578)
  Polish Share Compute settings (#3735)
  fix(reactions): wrap long popover names (#3834)
  fix(desktop): clarify inherited agent parallelism (#4010)
  feat(desktop): make onboarding model defaults skippable (#3968)
  ci: add guarded desktop release cache prewarm (#4575)
  fix(mobile): recover stale relay sessions (#4372)
  chore(release): release Buzz Desktop version 0.5.4 (#4562)
  test(mobile): assert follow boundary semantics (#4559)
  docs(release): align desktop handoff instructions (#3988)
  fix: report agent usage per provider round, not once per turn (#4545)
  fix(desktop): harden Windows installs against Defender block and orphaned Node (#4382)
  feat(desktop): improve channel template discovery (#4549)
  fix(desktop): save key backups to authorized path (#4022)
  Add channel activity hover menu (#3935)
  feat(desktop): show saved Run on settings when editing an agent (#4539)
  fix(desktop): disambiguate provider API key labels and annotate mint key (#4406)
  fix(desktop): make OpenAI key re-enterable after first save in card mint dialog (#4140)
  fix(config-bridge): add harness-definition env tier and fix equal-value model override (#3580)
  Polish mobile composer and messaging UI (#3918)
  ...

Signed-off-by: npub1223z34hd7vtwc6qj4s7flsxkj644nlre2nthu7lrrmkumhu3xddsrx9r6w <52a228d6edf316ec6812ac3c9fc0d696ab59fc7954d77e7be31eedcddf91335b@buzz.block.builderlab.xyz>
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>
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