Polish glass Huddle tray behavior (#5590)

## Summary

- inset the in-app Huddle tray with four rounded corners and even 8px
spacing when Glass background is enabled
- keep the popped-out Huddle dock full-width
- hide and suppress Glass background on Linux

## Why

The in-app tray reused the opaque backing needed by non-glass windows,
which covered the native vibrancy around it. Linux does not support this
window treatment.

## Testing

- `pnpm -C desktop build:e2e`
- focused Appearance and Huddle Playwright smoke tests
- pre-push desktop checks, typecheck, and 4,666 unit tests

---------

Signed-off-by: kenny lopez <klopez4212@gmail.com>
Signed-off-by: Princess Donut <b238ea756dee4d98afa5883fc7f1de61eeabe65bf700e3a5a5a80db5e42e2c2b@buzz.block.builderlab.xyz>
Co-authored-by: Princess Donut <b238ea756dee4d98afa5883fc7f1de61eeabe65bf700e3a5a5a80db5e42e2c2b@buzz.block.builderlab.xyz>
This commit is contained in:
klopez4212
2026-08-13 15:40:50 -07:00
committed by GitHub
co-authored by Princess Donut
parent 76f114a252
commit 0571f5455b
7 changed files with 262 additions and 23 deletions
+1 -1
View File
@@ -68,7 +68,7 @@ export function AppHuddleShell({
{children}
</div>
{isRoom || !isCompanionOpen ? (
<div className="absolute inset-x-0 bottom-0 z-[2] h-(--buzz-huddle-drawer-height)">
<div className="buzz-huddle-drawer-slot absolute inset-x-0 bottom-0 z-[2] h-(--buzz-huddle-drawer-height)">
<AppHuddleBar
mode={isRoom ? "room" : "main"}
onOpenHuddleWindow={isRoom ? undefined : onCompanionOpen}
@@ -13,6 +13,7 @@ import {
useLinkPreviewStyle,
type LinkPreviewStyle,
} from "@/shared/lib/linkPreviewStylePreference";
import { isLinuxPlatform } from "@/shared/lib/platform";
import {
ACCENT_COLORS,
DEFAULT_GLASS_OPACITY,
@@ -168,6 +169,9 @@ export function GlassBackgroundSetting() {
setGlassOpacity,
} = useTheme();
const shouldReduceMotion = useReducedMotion();
if (isLinuxPlatform()) return null;
const shouldShowOpacity = glassBackgroundSupported && glassBackground;
const opacityRow = (
<SettingsOptionRow data-testid="glass-opacity-row">
@@ -7,6 +7,7 @@
.buzz-huddle-shell {
--buzz-huddle-drawer-height: 5rem;
--buzz-huddle-drawer-inset: 8px;
--buzz-huddle-drawer-radius: 24px;
--buzz-huddle-drawer-ease: cubic-bezier(0.32, 0.72, 0, 1);
--buzz-huddle-drawer-surface: var(--huddle-drawer-surface, 0 0% 0%);
+38 -3
View File
@@ -901,7 +901,9 @@
background: transparent;
}
:root[data-glass-background] .buzz-huddle-app-surface-open::after {
:root[data-glass-background]
.buzz-huddle-shell[data-huddle-window="true"]
.buzz-huddle-app-surface-open::after {
background: hsl(var(--buzz-huddle-drawer-surface));
}
@@ -914,8 +916,8 @@
}
:root[data-buzz-sidebar][data-glass-background]
.buzz-huddle-shell[data-huddle-open="true"] {
background-color: hsl(var(--buzz-huddle-drawer-surface));
.buzz-huddle-shell[data-huddle-window="false"][data-huddle-open="true"] {
background-color: transparent;
background-image: none;
}
@@ -930,3 +932,36 @@
position: absolute;
z-index: 0;
}
/* In the main glass window, let vibrancy continue around the Huddle controls
* and present the tray as an inset four-corner surface. The dedicated Huddle
* window keeps the full-width dark control dock above. */
:root[data-glass-background]
.buzz-huddle-shell[data-huddle-window="false"]
.buzz-huddle-drawer-backdrop {
background: transparent;
}
:root[data-buzz-sidebar][data-glass-background]
.buzz-huddle-shell[data-huddle-window="false"][data-huddle-open="true"]::before {
content: none;
}
:root[data-glass-background]
.buzz-huddle-shell[data-huddle-window="false"]
.buzz-huddle-drawer-slot {
padding: var(--buzz-huddle-drawer-inset);
}
:root[data-glass-background]
.buzz-huddle-shell[data-huddle-window="false"]
.buzz-huddle-drawer-slot
> .buzz-huddle-drawer {
border-radius: calc(
var(--buzz-huddle-drawer-radius) -
var(--buzz-huddle-drawer-inset)
);
height: 100%;
padding-block: var(--buzz-huddle-drawer-inset);
padding-inline: var(--buzz-huddle-drawer-inset);
}
+35 -19
View File
@@ -361,6 +361,14 @@ async function applyWindowGlass(enabled: boolean) {
glassBackgroundPreferenceEnabled = enabled;
const requestToken = ++glassVibrancyRequest;
if (!isTauri() || !isMacPlatform()) {
glassBackgroundPreferenceEnabled = false;
glassVibrancyEnabled = false;
glassVibrancyReady = false;
setGlassBackgroundActive(false);
return;
}
if (enabled && glassVibrancyEnabled && glassVibrancyReady) {
maybeEnableGlassBackground(requestToken);
return;
@@ -368,11 +376,6 @@ async function applyWindowGlass(enabled: boolean) {
glassVibrancyReady = false;
if (!isTauri()) {
setGlassBackgroundActive(false);
return;
}
try {
await invokeTauri<void>("set_window_vibrancy", {
enabled,
@@ -478,6 +481,8 @@ export function ThemeProvider({
children,
defaultTheme = "buzz",
}: ThemeProviderProps) {
const glassBackgroundSupported = isTauri() && isMacPlatform();
// Apply cached vars synchronously before first render
const [selectedTheme, setSelectedTheme] = useState<string>(() => {
applyCachedVars();
@@ -499,8 +504,9 @@ export function ThemeProvider({
const [glassBackground, setGlassBackgroundState] = useState<boolean>(() => {
const stored = getStorageItem(GLASS_BACKGROUND_STORAGE_KEY);
// Glass is opt-in. Explicitly saved preferences remain intact, while a
// fresh profile starts with the normal opaque window treatment.
const enabled = stored === "true";
// fresh profile starts with the normal opaque window treatment. Keep an
// unsupported platform opaque without erasing a preference saved on Mac.
const enabled = glassBackgroundSupported && stored === "true";
glassBackgroundPreferenceEnabled = enabled;
return enabled;
});
@@ -668,17 +674,27 @@ export function ThemeProvider({
[],
);
const setGlassBackground = useCallback((enabled: boolean) => {
window.localStorage.setItem(
GLASS_BACKGROUND_STORAGE_KEY,
enabled ? "true" : "false",
);
glassBackgroundPreferenceEnabled = enabled;
if (!enabled) {
setGlassBackgroundActive(false);
}
setGlassBackgroundState(enabled);
}, []);
const setGlassBackground = useCallback(
(enabled: boolean) => {
if (!glassBackgroundSupported) {
glassBackgroundPreferenceEnabled = false;
setGlassBackgroundActive(false);
setGlassBackgroundState(false);
return;
}
window.localStorage.setItem(
GLASS_BACKGROUND_STORAGE_KEY,
enabled ? "true" : "false",
);
glassBackgroundPreferenceEnabled = enabled;
if (!enabled) {
setGlassBackgroundActive(false);
}
setGlassBackgroundState(enabled);
},
[glassBackgroundSupported],
);
const setGlassOpacity = useCallback((opacity: number) => {
const nextOpacity = clampGlassOpacity(opacity);
@@ -704,7 +720,7 @@ export function ThemeProvider({
followSystem,
glassBackground,
glassOpacity,
glassBackgroundSupported: isTauri() && isMacPlatform(),
glassBackgroundSupported,
prominentActiveTab,
hasPair,
terminalPalette,
@@ -478,6 +478,13 @@ test("appearance groups theme and preferences into labeled rows", async ({
page,
}) => {
await seedTheme(page, "github-light");
await page.addInitScript((prominentActiveTabStorageKey) => {
window.localStorage.setItem(prominentActiveTabStorageKey, "true");
Object.defineProperty(navigator, "platform", {
configurable: true,
get: () => "MacIntel",
});
}, PROMINENT_ACTIVE_TAB_STORAGE_KEY);
await installMockBridge(page);
await openAppearance(page, "light");
const themeCard = page.getByTestId("appearance-theme-card");
@@ -539,6 +546,22 @@ test("appearance groups theme and preferences into labeled rows", async ({
await themeCard.getByTestId("theme-option-buzz").click();
await expect(themeStyleTrigger).toHaveAttribute("aria-expanded", "true");
await expect(themeStyleOptions).toBeVisible();
await expect(
themeCard.getByTestId("prominent-active-tab-toggle"),
).toBeChecked();
const glassBeforeProminent = await themeCard.evaluate((card) => {
const glass = card.querySelector('[data-testid="glass-background-toggle"]');
const prominent = card.querySelector(
'[data-testid="prominent-active-tab-row"]',
);
return Boolean(
glass &&
prominent &&
glass.compareDocumentPosition(prominent) &
Node.DOCUMENT_POSITION_FOLLOWING,
);
});
expect(glassBeforeProminent).toBe(true);
const colorModeLabelBox = await themeCard
.getByTestId("appearance-color-mode-row")
@@ -1127,6 +1150,49 @@ test("glass background keeps the content panel solid", async ({ page }) => {
.toBe("false");
});
test("glass background is unavailable on Linux", async ({ page }) => {
await seedTheme(page, "buzz");
await page.addInitScript((storageKey) => {
window.localStorage.setItem(storageKey, "true");
(window as typeof window & { isTauri?: boolean }).isTauri = true;
Object.defineProperty(navigator, "platform", {
configurable: true,
get: () => "Linux x86_64",
});
Object.defineProperty(navigator, "userAgent", {
configurable: true,
get: () => "Buzz Desktop Linux",
});
}, GLASS_BACKGROUND_STORAGE_KEY);
await installMockBridge(page);
await openAppearance(page, "light");
await expect(page.getByTestId("glass-background-row")).toHaveCount(0);
await expect(page.getByTestId("glass-background-toggle")).toHaveCount(0);
await expect(page.getByTestId("glass-opacity-slider")).toHaveCount(0);
await expect(page.locator("html")).not.toHaveAttribute(
"data-glass-background",
"",
);
await expect
.poll(() =>
page.evaluate(() =>
(window.__BUZZ_E2E_COMMAND_LOG__ ?? []).some(
(entry) => entry.command === "set_window_vibrancy",
),
),
)
.toBe(false);
await expect
.poll(() =>
page.evaluate(
(storageKey) => window.localStorage.getItem(storageKey),
GLASS_BACKGROUND_STORAGE_KEY,
),
)
.toBe("true");
});
test("non-Buzz glass preserves the selected theme sidebar tint", async ({
page,
}) => {
@@ -1178,6 +1244,12 @@ test("accent picker reveals/hides when toggling Buzz", async ({ page }) => {
// non-Buzz tile brings it back. Asserts the presence toggle (the motion
// wrapper) works end to end.
await seedTheme(page, "github-light");
await page.addInitScript(() => {
Object.defineProperty(navigator, "platform", {
configurable: true,
get: () => "MacIntel",
});
});
await installMockBridge(page);
await openAppearance(page, "light");
await expect(page.getByTestId("accent-color-neutral")).toBeVisible();
@@ -264,6 +264,117 @@ test("keeps the drawer open until the huddle is expanded", async ({ page }) => {
expect(closedGradient).toBe(openGradient);
});
test("floats the in-app huddle tray over the glass background", async ({
page,
}) => {
await page.addInitScript(() => {
window.localStorage.setItem("buzz-theme", "buzz-dark");
window.localStorage.setItem("buzz-glass-background", "true");
(window as typeof window & { isTauri?: boolean }).isTauri = true;
Object.defineProperty(navigator, "platform", {
configurable: true,
get: () => "MacIntel",
});
});
await installMockBridge(page, {
huddle: {
parentChannelId: HUDDLE_PARENT_ID,
ephemeralChannelId: HUDDLE_CHANNEL_ID,
members: [
{ pubkey: TEST_IDENTITIES.tyler.pubkey, role: "member" },
{ pubkey: TEST_IDENTITIES.alice.pubkey, role: "bot" },
],
transcriptionEnabled: true,
},
});
await page.goto("/");
const root = page.locator("html");
const shell = page.locator('.buzz-huddle-shell[data-huddle-window="false"]');
const slot = shell.locator(".buzz-huddle-drawer-slot");
const drawer = slot.locator(":scope > .buzz-huddle-drawer");
const backdrop = shell.locator(".buzz-huddle-drawer-backdrop");
const transcriptButton = drawer.getByRole("button", {
name: "Stop transcript",
});
await expect(root).toHaveAttribute("data-glass-background", "");
await expect(shell).toHaveAttribute("data-huddle-open", "true");
await expect(shell).toHaveCSS("background-color", "rgba(0, 0, 0, 0)");
await expect(backdrop).toHaveCSS("background-color", "rgba(0, 0, 0, 0)");
await expect(drawer).toHaveCSS("border-top-left-radius", "16px");
await expect(drawer).toHaveCSS("border-top-right-radius", "16px");
await expect(drawer).toHaveCSS("border-bottom-right-radius", "16px");
await expect(drawer).toHaveCSS("border-bottom-left-radius", "16px");
await expect(drawer).toHaveCSS("padding-top", "8px");
await expect(drawer).toHaveCSS("padding-right", "8px");
await expect(drawer).toHaveCSS("padding-bottom", "8px");
await expect(drawer).toHaveCSS("padding-left", "8px");
const geometry = await Promise.all([
slot.boundingBox(),
drawer.boundingBox(),
transcriptButton.boundingBox(),
]);
const [slotBox, drawerBox, transcriptButtonBox] = geometry;
expect(slotBox).not.toBeNull();
expect(drawerBox).not.toBeNull();
expect(transcriptButtonBox).not.toBeNull();
if (!slotBox || !drawerBox || !transcriptButtonBox) return;
expect(drawerBox.x - slotBox.x).toBe(8);
expect(drawerBox.y - slotBox.y).toBe(8);
expect(slotBox.x + slotBox.width - (drawerBox.x + drawerBox.width)).toBe(8);
expect(slotBox.y + slotBox.height - (drawerBox.y + drawerBox.height)).toBe(8);
expect(
Math.abs(
transcriptButtonBox.y +
transcriptButtonBox.height / 2 -
(drawerBox.y + drawerBox.height / 2),
),
).toBeLessThanOrEqual(1);
});
test("keeps the popped-out huddle dock full-width over glass", async ({
page,
}) => {
await page.addInitScript(() => {
window.localStorage.setItem("buzz-theme", "buzz-dark");
window.localStorage.setItem("buzz-glass-background", "true");
(window as typeof window & { isTauri?: boolean }).isTauri = true;
Object.defineProperty(navigator, "platform", {
configurable: true,
get: () => "MacIntel",
});
});
await installMockBridge(page, {
windowLabel: `huddle-${HUDDLE_CHANNEL_ID}`,
huddle: {
parentChannelId: HUDDLE_PARENT_ID,
ephemeralChannelId: HUDDLE_CHANNEL_ID,
members: [
{ pubkey: TEST_IDENTITIES.tyler.pubkey, role: "member" },
{ pubkey: TEST_IDENTITIES.alice.pubkey, role: "bot" },
],
transcriptionEnabled: true,
},
});
await page.goto("/");
const root = page.locator("html");
const shell = page.locator('.buzz-huddle-shell[data-huddle-window="true"]');
const slot = shell.locator(".buzz-huddle-drawer-slot");
const drawer = slot.locator(":scope > .buzz-huddle-drawer");
await expect(root).toHaveAttribute("data-glass-background", "");
await expect(shell).toHaveAttribute("data-huddle-open", "true");
await expect(shell).not.toHaveCSS("background-color", "rgba(0, 0, 0, 0)");
await expect(slot).toHaveCSS("padding-top", "0px");
await expect(drawer).toHaveCSS("border-top-left-radius", "0px");
await expect(drawer).toHaveCSS("border-top-right-radius", "0px");
});
test("shows speaker identity on every huddle chat message", async ({
page,
}) => {