Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion desktop/src/app/AppHuddleShell.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
useLinkPreviewStyle,
type LinkPreviewStyle,
} from "@/shared/lib/linkPreviewStylePreference";
import { isLinuxPlatform } from "@/shared/lib/platform";
import {
ACCENT_COLORS,
DEFAULT_GLASS_OPACITY,
Expand Down Expand Up @@ -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">
Expand Down
1 change: 1 addition & 0 deletions desktop/src/shared/styles/globals/components.css
Original file line number Diff line number Diff line change
Expand Up @@ -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%);
Expand Down
41 changes: 38 additions & 3 deletions desktop/src/shared/styles/globals/theme.css
Original file line number Diff line number Diff line change
Expand Up @@ -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));
}

Expand All @@ -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;
}

Expand All @@ -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);
}
54 changes: 35 additions & 19 deletions desktop/src/shared/theme/ThemeProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -361,18 +361,21 @@ 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;
}

glassVibrancyReady = false;

if (!isTauri()) {
setGlassBackgroundActive(false);
return;
}

try {
await invokeTauri<void>("set_window_vibrancy", {
enabled,
Expand Down Expand Up @@ -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();
Expand All @@ -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;
});
Expand Down Expand Up @@ -663,17 +669,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);
Expand All @@ -699,7 +715,7 @@ export function ThemeProvider({
followSystem,
glassBackground,
glassOpacity,
glassBackgroundSupported: isTauri() && isMacPlatform(),
glassBackgroundSupported,
prominentActiveTab,
hasPair,
terminalPalette,
Expand Down
72 changes: 72 additions & 0 deletions desktop/tests/e2e/buzz-theme-screenshots.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down Expand Up @@ -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")
Expand Down Expand Up @@ -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,
}) => {
Expand Down Expand Up @@ -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();
Expand Down
Loading
Loading