From 880ece3740dc33660bbac0734167632ab63c01ed Mon Sep 17 00:00:00 2001 From: Kyle Zemek Date: Thu, 5 Nov 2026 17:48:13 -0800 Subject: [PATCH 1/2] fix(menu): stop clipping lifecycle actions behind a hidden scrollbar The shared menu popup capped its height at 24rem. With enlarged text, an agent-owned Solo chat's 11-row menu exceeded the cap, so Delete chat sat below the fold behind macOS's hidden overlay scrollbar and looked absent. Bound the menu by the available viewport height only. Fixes NEXUS-4264 Co-Authored-By: Claude Opus 5.5 Signed-off-by: Kyle Zemek --- .../design-system/styles/components.css | 4 ++- tests/browser/channel-lifecycle.spec.mjs | 36 +++++++++++++++++++ 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/src/shared/design-system/styles/components.css b/src/shared/design-system/styles/components.css index debea6000f..1550e88afd 100644 --- a/src/shared/design-system/styles/components.css +++ b/src/shared/design-system/styles/components.css @@ -2094,7 +2094,9 @@ .buzz-menu-popup { min-width: 11.5rem; max-width: min(20rem, var(--available-width), calc(100vw - var(--space-4))); - max-height: min(24rem, var(--available-height)); + /* Only the viewport bounds a menu: macOS hides overlay scrollbars until scrolled, + so rows past a fixed cap look absent. */ + max-height: var(--available-height); overflow-y: auto; @apply floating-surface; color: var(--text-standard); diff --git a/tests/browser/channel-lifecycle.spec.mjs b/tests/browser/channel-lifecycle.spec.mjs index c4b0c2beeb..2db0c300dc 100644 --- a/tests/browser/channel-lifecycle.spec.mjs +++ b/tests/browser/channel-lifecycle.spec.mjs @@ -831,3 +831,39 @@ test.describe("remote Solo unarchive", () => { expect(app.report.unexpected).toEqual([]); }); }); + +// Browser-only: Playwright clicks scroll items into view, so only layout shows +// whether the shared menu clips a permitted action behind a hidden scrollbar. +test.describe("Solo chat owned through its agent", () => { + test.use({ + soloChannels: ["11111111-1111-4111-8111-111111111111"], + lifecycleRole: "member", + lifecycleOwnerAgent: true, + readState: true, + }); + test("shows every lifecycle action without scrolling the menu", async ({ + page, + app, + }) => { + await page.goto(app.origin); + await openPage(page, "Messages"); + const row = page + .getByRole("navigation", { name: "Subscribed channels" }) + .getByRole("button", { name: "Lifecycle channel", exact: true }); + await row.focus(); + await page.keyboard.press("Shift+F10"); + const menu = page.getByRole("menu", { + name: "Actions for Lifecycle channel", + }); + // The owner-agent grant offers all three to a plain member. + for (const name of ["Leave chat", "Archive chat", "Delete chat"]) + await expect( + menu.getByRole("menuitem", { name, exact: true }), + ).toBeInViewport({ ratio: 1 }); + expect( + await menu.evaluate((popup) => popup.scrollHeight - popup.clientHeight), + ).toBe(0); + expect(app.report.lifecyclePublications ?? []).toHaveLength(0); + expect(app.report.unexpected).toEqual([]); + }); +}); From 8d44b662cb3ad126d0d677ca1f4aa21a0d47e754 Mon Sep 17 00:00:00 2001 From: Kyle Zemek Date: Thu, 5 Nov 2026 18:04:19 -0800 Subject: [PATCH 2/2] fix(menu): say when a menu still scrolls; note the test's scale Address first-push review: the comment no longer implies a menu can never clip, and the journey notes why it runs at the default text scale. Co-Authored-By: Claude Opus 5.5 Signed-off-by: Kyle Zemek --- src/shared/design-system/styles/components.css | 4 ++-- tests/browser/channel-lifecycle.spec.mjs | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/shared/design-system/styles/components.css b/src/shared/design-system/styles/components.css index 1550e88afd..c249b72b26 100644 --- a/src/shared/design-system/styles/components.css +++ b/src/shared/design-system/styles/components.css @@ -2094,8 +2094,8 @@ .buzz-menu-popup { min-width: 11.5rem; max-width: min(20rem, var(--available-width), calc(100vw - var(--space-4))); - /* Only the viewport bounds a menu: macOS hides overlay scrollbars until scrolled, - so rows past a fixed cap look absent. */ + /* No fixed cap: macOS hides overlay scrollbars until scrolled, so rows past one look + absent. Only a viewport too short for the menu still scrolls. */ max-height: var(--available-height); overflow-y: auto; @apply floating-surface; diff --git a/tests/browser/channel-lifecycle.spec.mjs b/tests/browser/channel-lifecycle.spec.mjs index 2db0c300dc..0153d656e9 100644 --- a/tests/browser/channel-lifecycle.spec.mjs +++ b/tests/browser/channel-lifecycle.spec.mjs @@ -860,6 +860,8 @@ test.describe("Solo chat owned through its agent", () => { await expect( menu.getByRole("menuitem", { name, exact: true }), ).toBeInViewport({ ratio: 1 }); + // At the default text scale, the old 24rem cap clipped this menu by its + // fixed-pixel parts; larger text grows rows and a rem cap alike. expect( await menu.evaluate((popup) => popup.scrollHeight - popup.clientHeight), ).toBe(0);