From d1efad1d3f6a7d39d24cb00768243d377cd06aa6 Mon Sep 17 00:00:00 2001 From: Wes Date: Thu, 25 Jun 2026 09:40:38 -0600 Subject: [PATCH 1/2] Fix mention autocomplete layout in narrow threads Keep mention suggestion avatars visible while making the display name the primary row content. Move agent/role/owner metadata into a smaller secondary line so narrow thread panels preserve readable names without letting metadata crowd the row. Add an e2e regression that opens a minimum-width thread panel and verifies multiple long agent names remain readable with avatars and metadata present. Co-authored-by: Pinky <44b8e82baa6e0e254e0208d68f335c283c94e7b78dd1fa10d5a49d3f13dd0435@sprout-oss.stage.blox.sqprod.co> Signed-off-by: Wes --- .../messages/ui/MentionAutocomplete.tsx | 74 +++++++----- desktop/tests/e2e/mentions.spec.ts | 112 ++++++++++++++++++ 2 files changed, 156 insertions(+), 30 deletions(-) diff --git a/desktop/src/features/messages/ui/MentionAutocomplete.tsx b/desktop/src/features/messages/ui/MentionAutocomplete.tsx index 9eec93220f..84ac5183cf 100644 --- a/desktop/src/features/messages/ui/MentionAutocomplete.tsx +++ b/desktop/src/features/messages/ui/MentionAutocomplete.tsx @@ -96,44 +96,58 @@ export const MentionAutocomplete = React.memo(function MentionAutocomplete({ avatarUrl={suggestion.avatarUrl ?? null} displayName={suggestion.displayName} size="xs" + testId="mention-suggestion-avatar" /> - - - - {suggestion.displayName} - - {suggestion.isAgent ? ( - - - ) : suggestion.role ? ( - {suggestion.role} - ) : null} + + + {suggestion.displayName} - {suggestion.ownerLabel || suggestion.notInChannel ? ( + {suggestion.isAgent || + suggestion.role || + suggestion.ownerLabel || + suggestion.notInChannel ? ( - {suggestion.ownerLabel - ? `owned by ${suggestion.ownerLabel}${suggestion.notInChannel ? " · not in channel" : ""}` - : "not in channel"} + {suggestion.isAgent ? ( + + + ) : suggestion.role ? ( + + {suggestion.role} + + ) : null} + {suggestion.ownerLabel || suggestion.notInChannel ? ( + + {suggestion.ownerLabel + ? `owned by ${suggestion.ownerLabel}${suggestion.notInChannel ? " · not in channel" : ""}` + : "not in channel"} + + ) : null} ) : null} diff --git a/desktop/tests/e2e/mentions.spec.ts b/desktop/tests/e2e/mentions.spec.ts index c969bd2a03..1e73743e51 100644 --- a/desktop/tests/e2e/mentions.spec.ts +++ b/desktop/tests/e2e/mentions.spec.ts @@ -46,6 +46,46 @@ function commandCount(commands: string[], command: string) { return commands.filter((entry) => entry === command).length; } +async function emitMockMessage( + page: import("@playwright/test").Page, + channelName: string, + content: string, + options?: { + parentEventId?: string; + pubkey?: string; + }, +) { + const event = await page.evaluate( + ({ ch, msg, parentEventId, pubkey }) => { + return ( + window as Window & { + __BUZZ_E2E_EMIT_MOCK_MESSAGE__?: (input: { + channelName: string; + content: string; + parentEventId?: string | null; + pubkey?: string; + }) => { id: string; created_at: number; pubkey: string }; + } + ).__BUZZ_E2E_EMIT_MOCK_MESSAGE__?.({ + channelName: ch, + content: msg, + parentEventId: parentEventId ?? undefined, + pubkey: pubkey ?? undefined, + }); + }, + { + ch: channelName, + msg: content, + parentEventId: options?.parentEventId ?? null, + pubkey: options?.pubkey ?? TEST_IDENTITIES.alice.pubkey, + }, + ); + if (!event) { + throw new Error("Mock message emitter is not installed"); + } + return event; +} + async function waitForMockLiveSubscription( page: import("@playwright/test").Page, channelName: string, @@ -135,6 +175,78 @@ test("@ trigger shows unified autocomplete with agents first", async ({ expect(bobIndex).toBeLessThan(charlieIndex); }); +test("thread autocomplete keeps multiple long names readable in a narrow panel", async ({ + page, +}) => { + await installMockBridge(page, { + managedAgents: [ + { + pubkey: + "9999999999999999999999999999999999999999999999999999999999999999", + name: "Brain With A Very Long Name", + status: "stopped", + }, + { + pubkey: + "9999999999999999999999999999999999999999999999999999999999999998", + name: "Brainstorming Assistant With A Long Name", + status: "stopped", + }, + { + pubkey: + "9999999999999999999999999999999999999999999999999999999999999997", + name: "Brainy Helper With Another Long Name", + status: "stopped", + }, + ], + }); + await page.setViewportSize({ width: 900, height: 640 }); + await page.addInitScript(() => { + window.sessionStorage.setItem("buzz.desktop.thread-panel-width", "300"); + }); + await page.goto("/"); + await page.getByTestId("channel-general").click(); + await expect(page.getByTestId("chat-title")).toHaveText("general"); + await page.setViewportSize({ width: 760, height: 640 }); + + await emitMockMessage(page, "general", "Reply to open the thread", { + parentEventId: "mock-general-welcome", + }); + const threadSummary = page.getByTestId("message-thread-summary").first(); + await expect(threadSummary).toBeVisible(); + await threadSummary.click(); + + const threadPanel = page.getByTestId("message-thread-panel"); + await expect(threadPanel).toBeVisible(); + const panelBox = await threadPanel.boundingBox(); + expect(panelBox?.width ?? Number.POSITIVE_INFINITY).toBeLessThanOrEqual(320); + + const input = threadPanel.getByTestId("message-input"); + await input.fill("@Brain"); + + const dropdown = threadPanel.getByTestId("mention-autocomplete"); + await expect(dropdown).toBeVisible(); + + for (const name of [ + "Brain With A Very Long Name", + "Brainstorming Assistant With A Long Name", + "Brainy Helper With Another Long Name", + ]) { + const row = dropdown.locator("button", { hasText: name }); + await expect(row).toBeVisible(); + await expect( + row.getByTestId("mention-suggestion-avatar-fallback"), + ).toBeVisible(); + await expect(row.getByText("agent")).toBeVisible(); + await expect(row.getByText(/owned by npub1mock/)).toBeVisible(); + + await expect(row.getByText(name)).not.toHaveCSS( + "text-overflow", + "ellipsis", + ); + } +}); + test("autocomplete filters suggestions as user types", async ({ page }) => { await page.goto("/"); await page.getByTestId("channel-general").click(); From fb2856acddffc189cb6810f958e057b8f18f308d Mon Sep 17 00:00:00 2001 From: Wes Date: Thu, 25 Jun 2026 09:48:45 -0600 Subject: [PATCH 2/2] Fix mention autocomplete metadata text token Use the shared text-2xs Tailwind token for autocomplete metadata so the desktop px-text guard passes while preserving the intended 11px visual size. Co-authored-by: Pinky <44b8e82baa6e0e254e0208d68f335c283c94e7b78dd1fa10d5a49d3f13dd0435@sprout-oss.stage.blox.sqprod.co> Signed-off-by: Wes --- desktop/src/features/messages/ui/MentionAutocomplete.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/desktop/src/features/messages/ui/MentionAutocomplete.tsx b/desktop/src/features/messages/ui/MentionAutocomplete.tsx index 84ac5183cf..3320e7ea1b 100644 --- a/desktop/src/features/messages/ui/MentionAutocomplete.tsx +++ b/desktop/src/features/messages/ui/MentionAutocomplete.tsx @@ -111,7 +111,7 @@ export const MentionAutocomplete = React.memo(function MentionAutocomplete({ suggestion.notInChannel ? (