Skip to content

Commit 4f4a6a7

Browse files
authored
fix(web): scope desktop vibrancy to macOS (#6171)
1 parent c2352b6 commit 4f4a6a7

4 files changed

Lines changed: 58 additions & 13 deletions

File tree

apps/web/src/App.tsx

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ import type {
144144
PluginShareProjectOutcome,
145145
WorkspaceProjectListView,
146146
} from './state/projects';
147-
import type { OpenDesignHostProjectImportSuccess } from '@open-design/host';
147+
import { getOpenDesignHost, type OpenDesignHostProjectImportSuccess } from '@open-design/host';
148148
import { useI18n } from './i18n';
149149
import { liveArtifactTabId } from './types';
150150
import type {
@@ -665,6 +665,7 @@ function AppInner() {
665665
const { t } = useI18n();
666666
const iframeKeepAlivePool = useIframeKeepAlivePool();
667667
const clientType = useMemo(() => detectClientType(), []);
668+
const hostPlatform = useMemo(() => getOpenDesignHost()?.client.platform, []);
668669
useModalWindowDragGuard();
669670
const workspaceContextState = useWorkspaceContext();
670671
const {
@@ -716,7 +717,11 @@ function AppInner() {
716717
// scrim to let the wallpaper show through more clearly; on focus the scrim
717718
// returns to full strength (app-wash.css keys off this class).
718719
useEffect(() => {
719-
if (clientType !== 'desktop' || typeof window === 'undefined') return undefined;
720+
if (
721+
clientType !== 'desktop'
722+
|| hostPlatform !== 'darwin'
723+
|| typeof window === 'undefined'
724+
) return undefined;
720725
const root = document.documentElement;
721726
const sync = () => root.classList.toggle('is-window-blurred', !document.hasFocus());
722727
sync();
@@ -727,7 +732,7 @@ function AppInner() {
727732
window.removeEventListener('blur', sync);
728733
root.classList.remove('is-window-blurred');
729734
};
730-
}, [clientType]);
735+
}, [clientType, hostPlatform]);
731736
const [config, setConfig] = useState<AppConfig>(() => loadConfig());
732737
const configRef = useRef(config);
733738
configRef.current = config;
@@ -3497,6 +3502,7 @@ function AppInner() {
34973502
<div
34983503
className={`workspace-shell workspace-shell--${clientType}`}
34993504
data-client-type={clientType}
3505+
data-host-platform={hostPlatform}
35003506
>
35013507
<WorkspaceTabsBar
35023508
route={route}

apps/web/src/styles/app-wash.css

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -76,9 +76,10 @@ body::before {
7676
wallpaper blurs through the window, so the painted pastel blobs step aside.
7777
html/body go fully transparent and the wash collapses to a translucent
7878
cream scrim that keeps content readable over arbitrary wallpapers — the
79-
Craft look. The .workspace-shell--desktop marker comes from the host
80-
bridge (App.tsx data-client-type). Browsers never match this block. */
81-
html:has(.workspace-shell--desktop) {
79+
Craft look. The desktop and darwin markers come from the host bridge
80+
(App.tsx data-client-type/data-host-platform). Windows desktop windows are
81+
opaque, so they intentionally keep the normal pastel wash. */
82+
html:has(.workspace-shell--desktop[data-host-platform='darwin']) {
8283
/* Focused window: a firm scrim so content sits on a mostly solid ground.
8384
The unfocused state below thins this back to ~21% (0.34 × 62%), which
8485
preserves the airy see-through look while the window is in the back. */
@@ -88,17 +89,17 @@ html:has(.workspace-shell--desktop) {
8889
);
8990
background: transparent;
9091
}
91-
html:has(.workspace-shell--desktop) body {
92+
html:has(.workspace-shell--desktop[data-host-platform='darwin']) body {
9293
background: transparent;
9394
}
9495
/* Focus response: while the window is unfocused (App.tsx toggles
9596
.is-window-blurred on <html> in desktop mode) the scrim thins out so the
9697
wallpaper reads clearly through the glass; regaining focus fades it back
9798
to full strength. Browsers never get the class, so web mode is untouched. */
98-
html:has(.workspace-shell--desktop) body::before {
99+
html:has(.workspace-shell--desktop[data-host-platform='darwin']) body::before {
99100
transition: opacity 240ms cubic-bezier(0.23, 1, 0.32, 1);
100101
}
101-
html.is-window-blurred:has(.workspace-shell--desktop) body::before {
102+
html.is-window-blurred:has(.workspace-shell--desktop[data-host-platform='darwin']) body::before {
102103
opacity: 0.34;
103104
}
104105
/* The top project tab strip stays flush and seamless with the page below it:
@@ -126,8 +127,8 @@ html.is-window-blurred:has(.workspace-shell--desktop) body::before {
126127
--veil-canvas: var(--bg);
127128
--veil-panel: var(--bg-panel);
128129
}
129-
html:has(.workspace-shell--desktop),
130-
html:has(.workspace-shell--desktop) body {
130+
html:has(.workspace-shell--desktop[data-host-platform='darwin']),
131+
html:has(.workspace-shell--desktop[data-host-platform='darwin']) body {
131132
background: var(--bg-app);
132133
}
133134
}
@@ -143,8 +144,8 @@ html.is-window-blurred:has(.workspace-shell--desktop) body::before {
143144
--veil-canvas: var(--bg);
144145
--veil-panel: var(--bg-panel);
145146
}
146-
html:has(.workspace-shell--desktop),
147-
html:has(.workspace-shell--desktop) body {
147+
html:has(.workspace-shell--desktop[data-host-platform='darwin']),
148+
html:has(.workspace-shell--desktop[data-host-platform='darwin']) body {
148149
background: var(--bg-app);
149150
}
150151
}

apps/web/tests/components/App.update-dialog.test.tsx

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,23 @@ describe('App updater dialog integration', () => {
204204
vi.clearAllMocks();
205205
});
206206

207+
it('exposes the desktop host platform on the workspace shell', () => {
208+
restoreHost = installMockOpenDesignHost({
209+
host: {
210+
client: {
211+
platform: 'win32',
212+
},
213+
},
214+
});
215+
216+
const { container } = render(<App />);
217+
218+
expect(container.querySelector('.workspace-shell')).toHaveAttribute(
219+
'data-host-platform',
220+
'win32',
221+
);
222+
});
223+
207224
it('mounts the updater open-dialog subscription and handles the mac app menu request', async () => {
208225
let openDialogListener: OpenDesignHostUpdaterOpenDialogListener | null = null;
209226
const check = vi.fn(async () => idleStatus({ state: 'not-available' }));
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { readFileSync } from 'node:fs';
2+
import { describe, expect, it } from 'vitest';
3+
4+
const appWashCss = readFileSync(
5+
new URL('../../src/styles/app-wash.css', import.meta.url),
6+
'utf8',
7+
);
8+
9+
describe('desktop app wash platform contract', () => {
10+
it('limits window-vibrancy material rules to macOS desktop hosts', () => {
11+
const macDesktopSelector =
12+
":has(.workspace-shell--desktop[data-host-platform='darwin'])";
13+
const vibrancySelectors = appWashCss.match(
14+
/html(?:\.is-window-blurred)?:has\(\.workspace-shell--desktop[^)]*\)(?: body(?:::before)?)?/g,
15+
);
16+
17+
expect(vibrancySelectors).not.toBeNull();
18+
expect(vibrancySelectors).not.toHaveLength(0);
19+
expect(vibrancySelectors?.every((selector) => selector.includes(macDesktopSelector))).toBe(true);
20+
});
21+
});

0 commit comments

Comments
 (0)