fix(desktop): more compact "compact" link previews (#5629)

**Category:** improvement
**User Impact:** Compact link previews now use a single-line title and
smaller thumbnail, making conversations easier to scan.

**Problem:** Compact previews gave long titles and oversized thumbnails
too much visual weight in the message timeline.

**Solution:** Keep titles to one ellipsized line and reduce image
thumbnails to a 104×64 treatment while preserving the existing wide
aspect ratio; Rich previews remain unchanged.

<details>
<summary>File changes</summary>

**desktop/src/shared/ui/compact-link-preview-attachment.tsx**
Tightens the Compact presentation with a single-line title and smaller
wide thumbnail, leaving Rich previews untouched.

**desktop/tests/e2e/messaging.spec.ts**
Adds focused coverage for title overflow, exact 64px card and 104×64
thumbnail geometry, and successful decoded-image rendering using a
realistic fixture, plus an optional visual capture.

**desktop/tests/fixtures/github-pr-5629-og.png**
Provides realistic visible image bytes for the compact-preview
image-rendering E2E path.

</details>

## Reproduction steps

1. Launch the desktop app with link preview style set to Compact.
2. Send a link whose preview has an image and a long title.
3. Confirm the thumbnail renders at the smaller wide size and the title
truncates to one line with an ellipsis.
4. Switch link preview style to Rich and confirm its presentation is
unchanged.

## Screenshot

![Compact link preview at 64px tall with a decoded real-image thumbnail
and one-line truncated
title](https://d24qwcpro867f5.cloudfront.net/repos/buzz/prs/5629/compact-link-preview-real-image-64px.png)

---------

Signed-off-by: Taylor Ho <taylorkmho@gmail.com>
Co-authored-by: Carl <acda9e433d19dcd0e6b6840f7f4b98f3a56f1fab98049d444c087019e6d36560@buzz.block.builderlab.xyz>
This commit is contained in:
Taylor Ho
2026-08-13 10:51:49 -07:00
committed by GitHub
co-authored by Carl
parent 98d3d77b42
commit 45f4b91a36
3 changed files with 61 additions and 6 deletions
@@ -79,7 +79,7 @@ export function CompactLinkPreviewAttachment({
className={cn(
"w-full bg-transparent no-underline shadow-none hover:bg-transparent",
reserveImage
? "h-21 min-h-21 max-h-21 gap-0 border-0 p-0 hover:border-transparent"
? "gap-0 border-0 px-0 py-0 hover:border-transparent"
: "rounded-none border-0 border-l-[3px] border-border px-0 py-1 pl-3 hover:border-border",
)}
data-image-state={preview.imageState}
@@ -89,7 +89,7 @@ export function CompactLinkPreviewAttachment({
{reserveImage ? (
<AttachmentMedia
aria-hidden={showImage ? undefined : "true"}
className="aspect-auto h-full min-h-0 w-30 min-w-30 max-w-30 self-stretch rounded-xl bg-muted sm:w-34 sm:min-w-34 sm:max-w-34"
className="aspect-auto h-16 w-26 rounded-xl bg-muted"
data-link-preview-thumbnail=""
variant="image"
>
@@ -110,7 +110,7 @@ export function CompactLinkPreviewAttachment({
)}
</AttachmentMedia>
) : null}
<AttachmentContent className={reserveImage ? "px-2 py-2" : undefined}>
<AttachmentContent className={reserveImage ? "px-2 py-1.5" : undefined}>
<a
className="relative z-20 flex w-fit max-w-full min-w-0 items-center gap-1.5 text-xs font-normal leading-4 text-muted-foreground/70 group-hover/attachment:underline"
data-link-preview-hostname=""
@@ -145,7 +145,7 @@ export function CompactLinkPreviewAttachment({
) : null}
<span className="truncate">{hostname}</span>
</a>
<AttachmentTitle className="line-clamp-2 whitespace-normal group-hover/attachment:underline">
<AttachmentTitle className="group-hover/attachment:underline">
{preview.title}
</AttachmentTitle>
{preview.description ? (
+57 -2
View File
@@ -1,3 +1,5 @@
import { readFileSync } from "node:fs";
import { expect, test, type Locator } from "@playwright/test";
import { waitForAnimations } from "../helpers/animations";
@@ -5,6 +7,11 @@ import { installMockBridge, TEST_IDENTITIES } from "../helpers/bridge";
import { expectCornerRadiusPx, expectSmoothCorners } from "../helpers/css";
import { openSettings } from "../helpers/settings";
const LINK_PREVIEW_IMAGE = readFileSync(
new URL("../fixtures/github-pr-5629-og.png", import.meta.url),
);
const LINK_PREVIEW_IMAGE_DATA_URL = `data:image/png;base64,${LINK_PREVIEW_IMAGE.toString("base64")}`;
async function waitForReadyComposerSnapshots(
page: import("@playwright/test").Page,
count = 1,
@@ -208,8 +215,7 @@ test.beforeEach(async ({ page }, testInfo) => {
siteName: "GitHub",
description:
"A polished, stable preview for shared links.",
imageDataUrl:
"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNk+A8AAQUBAScY42YAAAAASUVORK5CYII=",
imageDataUrl: LINK_PREVIEW_IMAGE_DATA_URL,
imageDomain: "opengraph.githubassets.com",
faviconDataUrl:
"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNk+A8AAQUBAScY42YAAAAASUVORK5CYII=",
@@ -1302,6 +1308,55 @@ test("composer link preview embeds stay attachment-sized while loading and ready
}
});
test("compact link preview image geometry truncates long titles to one line", async ({
page,
}) => {
const previewUrl = "https://github.com/block/buzz/pull/3246?geometry=1";
await page.route("http://localhost:3000/media/*.png", (route) =>
route.fulfill({
body: LINK_PREVIEW_IMAGE,
contentType: "image/png",
}),
);
await page.setViewportSize({ width: 800, height: 700 });
await page.goto("/");
await page.getByTestId("channel-general").click();
await page.getByTestId("message-input").fill(previewUrl);
await waitForReadyComposerSnapshots(page);
await page.getByTestId("send-message").click();
const row = page.getByTestId("message-row").last();
const card = row.locator('[data-link-preview="github-pull-request"]');
const thumbnail = card.locator("[data-link-preview-thumbnail]");
const title = card.locator('[data-slot="attachment-title"]');
const image = thumbnail.locator("img");
await expect(card).toHaveAttribute("data-image-state", "image");
await expect(image).toBeVisible();
await expect
.poll(() => image.evaluate((element) => element.naturalWidth))
.toBeGreaterThan(0);
await expect(card).toHaveCSS("height", "64px");
await expect(thumbnail).toHaveCSS("height", "64px");
await expect(thumbnail).toHaveCSS("width", "104px");
await expect(title).toHaveText(
"Ship a wider horizontal preview with a two-line title that wraps cleanly",
);
await expect(title).toHaveCSS("white-space", "nowrap");
await expect
.poll(() =>
title.evaluate((element) => element.scrollWidth - element.clientWidth),
)
.toBeGreaterThan(1);
if (process.env.BUZZ_LINK_PREVIEW_SCREENSHOTS_DIR) {
await waitForAnimations(page);
await row.screenshot({
animations: "disabled",
path: `${process.env.BUZZ_LINK_PREVIEW_SCREENSHOTS_DIR}/recipient-compact-long-title.png`,
});
}
});
test("composer no-image link embeds keep the attachment footprint", async ({
page,
}) => {
Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB