refactor electron style setting for mac only

Made-with: Cursor
This commit is contained in:
Douglas 2026-03-20 15:10:56 +00:00
parent e0e7bcef27
commit aa22180b8a
5 changed files with 42 additions and 30 deletions

1
.gitignore vendored
View file

@ -42,6 +42,7 @@ yarn.lock
.env.development
.cursor
.claude
# Public directory (large media files)
public/

View file

@ -2718,6 +2718,8 @@ let installationLock: Promise<PromiseReturnType> = Promise.resolve({
// ==================== window create ====================
async function createWindow() {
const isMac = process.platform === 'darwin';
const isAppleSilicon = isMac && process.arch === 'arm64';
const supportsTransparency = isAppleSilicon;
// Ensure .eigent directories exist before anything else
ensureEigentDirectories();
@ -2749,23 +2751,23 @@ async function createWindow() {
height: 800,
minWidth: 1050,
minHeight: 650,
// Use native frame on Windows for better native integration
frame: isWindows ? true : false,
show: false, // Don't show until content is ready to avoid white screen
// Only use transparency on macOS and Linux (not supported well on Windows)
transparent: !isWindows,
// Solid background on Windows (respect dark/light mode), fully transparent on macOS for native vibrancy
backgroundColor: isWindows
? nativeTheme.shouldUseDarkColors
// Only use transparency on Apple Silicon Macs (older Macs/Windows/Linux have rendering issues)
transparent: supportsTransparency,
// Solid background for non-transparent mode, fully transparent on Apple Silicon for native vibrancy
backgroundColor: supportsTransparency
? '#00000000'
: nativeTheme.shouldUseDarkColors
? '#1e1e1e'
: '#ffffff'
: '#00000000',
: '#ffffff',
// macOS-specific title bar styling
titleBarStyle: isMac ? 'hidden' : undefined,
trafficLightPosition: isMac ? { x: 10, y: 10 } : undefined,
icon: path.join(VITE_PUBLIC, 'favicon.ico'),
// Rounded corners on macOS and Linux (as original)
roundedCorners: !isWindows,
// Rounded corners on Apple Silicon Macs (transparent mode)
roundedCorners: supportsTransparency,
// Non-transparent platforms need a frame except macOS (which uses hidden titleBarStyle)
frame: isMac ? false : isWindows ? true : false,
// Windows-specific options
...(isWindows && {
autoHideMenuBar: true, // Hide menu bar on Windows for cleaner look
@ -2783,8 +2785,8 @@ async function createWindow() {
},
});
// Apply native macOS effects
if (process.platform === 'darwin') {
// Apply native macOS visual effects only on Apple Silicon (transparent mode support)
if (supportsTransparency) {
win.once('ready-to-show', () => {
if (win && !win.isDestroyed()) {
try {
@ -2797,7 +2799,7 @@ async function createWindow() {
// Make titlebar transparent
setTransparentTitlebar(win);
log.info('[MacOS] Applied native visual effects');
log.info('[MacOS] Applied native visual effects (Apple Silicon)');
} catch (error) {
log.error('[MacOS] Failed to apply native visual effects:', error);
}

View file

@ -53,6 +53,7 @@ contextBridge.exposeInMainWorld('electronAPI', {
onExecuteAction: (callback: (action: string) => void) =>
ipcRenderer.on('execute-action', (event, action) => callback(action)),
getPlatform: () => process.platform,
getArch: () => process.arch,
getHomeDir: () => ipcRenderer.invoke('get-home-dir'),
createWebView: (id: string, url: string) =>
ipcRenderer.invoke('create-webview', id, url),

View file

@ -82,7 +82,9 @@ export default function SettingGeneral() {
useEffect(() => {
const platform = window.electronAPI.getPlatform();
console.log(platform);
const arch = window.electronAPI.getArch();
const isAppleSilicon = platform === 'darwin' && arch === 'arm64';
const baseThemes = [
{
img: dark,
@ -96,7 +98,7 @@ export default function SettingGeneral() {
},
];
if (platform === 'darwin') {
if (isAppleSilicon) {
setThemeList([
...baseThemes,
{
@ -107,6 +109,10 @@ export default function SettingGeneral() {
]);
} else {
setThemeList(baseThemes);
// If user previously had transparent mode on an unsupported device, fall back to dark
if (appearance === 'transparent') {
setAppearance('dark');
}
}
}, []);
@ -236,8 +242,8 @@ export default function SettingGeneral() {
return (
<div className="m-auto h-auto w-full flex-1">
{/* Header Section */}
<div className="mx-auto flex w-full max-w-[900px] items-center justify-between px-6 pb-6 pt-8">
<div className="flex w-full flex-row items-center justify-between gap-4">
<div className="px-6 pb-6 pt-8 mx-auto flex w-full max-w-[900px] items-center justify-between">
<div className="gap-4 flex w-full flex-row items-center justify-between">
<div className="flex flex-col">
<div className="text-heading-sm font-bold text-text-heading">
{t('setting.general')}
@ -246,10 +252,10 @@ export default function SettingGeneral() {
</div>
</div>
{/* Content Section */}
<div className="mb-xl flex flex-col gap-6">
<div className="mb-xl gap-6 flex flex-col">
{/* Profile Section */}
<div className="item-center flex flex-row justify-between rounded-2xl bg-surface-secondary px-6 py-4">
<div className="flex flex-col gap-2">
<div className="item-center rounded-2xl bg-surface-secondary px-6 py-4 flex flex-row justify-between">
<div className="gap-2 flex flex-col">
<div className="text-body-base font-bold text-text-heading">
{t('setting.profile')}
</div>
@ -263,7 +269,7 @@ export default function SettingGeneral() {
/>
</div>
</div>
<div className="flex items-center gap-sm">
<div className="gap-sm flex items-center">
<Button
onClick={() => {
window.location.href = `https://www.eigent.ai/dashboard?email=${authStore.email}`;
@ -294,7 +300,7 @@ export default function SettingGeneral() {
</div>
{/* Language Section */}
<div className="item-center flex flex-row justify-between rounded-2xl bg-surface-secondary px-6 py-4">
<div className="item-center rounded-2xl bg-surface-secondary px-6 py-4 flex flex-row justify-between">
<div className="flex flex-1 items-center">
<div className="text-body-base font-bold text-text-heading">
{t('setting.language')}
@ -304,7 +310,7 @@ export default function SettingGeneral() {
<SelectTrigger className="w-48">
<SelectValue placeholder={t('setting.select-language')} />
</SelectTrigger>
<SelectContent className="border bg-input-bg-default">
<SelectContent className="bg-input-bg-default border">
<SelectGroup>
<SelectItem value="system">
{t('setting.system-default')}
@ -320,20 +326,20 @@ export default function SettingGeneral() {
</div>
{/* Appearance Section */}
<div className="item-center flex flex-col justify-between gap-4 rounded-2xl bg-surface-secondary px-6 py-4">
<div className="item-center gap-4 rounded-2xl bg-surface-secondary px-6 py-4 flex flex-col justify-between">
<div className="text-body-base font-bold text-text-heading">
{t('setting.appearance')}
</div>
<div className="flex w-full flex-row items-center gap-md">
<div className="gap-md flex w-full flex-row items-center">
{themeList.map((item: any) => (
<div
key={item.label}
className="group flex w-full flex-col items-center gap-sm hover:cursor-pointer"
className="group gap-sm flex w-full flex-col items-center hover:cursor-pointer"
onClick={() => setAppearance(item.value)}
>
<img
src={item.img}
className={`group-hover:border-bg-fill-info-primary aspect-[183/91.67] w-full rounded-lg border border-solid border-transparent transition-all ${
className={`group-hover:border-bg-fill-info-primary rounded-lg aspect-[183/91.67] w-full border border-solid border-transparent transition-all ${
item.value == appearance
? 'border-bg-fill-info-primary'
: ''
@ -353,8 +359,8 @@ export default function SettingGeneral() {
</div>
{/* Network Proxy Section */}
<div className="flex flex-col gap-4 rounded-2xl bg-surface-secondary px-6 py-4">
<div className="flex flex-col gap-1">
<div className="gap-4 rounded-2xl bg-surface-secondary px-6 py-4 flex flex-col">
<div className="gap-1 flex flex-col">
<div className="text-body-base font-bold text-text-heading">
{t('setting.network-proxy')}
</div>

View file

@ -14,6 +14,7 @@
interface IpcRenderer {
getPlatform: () => string;
getArch: () => string;
minimizeWindow: () => void;
toggleMaximizeWindow: () => void;
closeWindow: () => void;
@ -50,6 +51,7 @@ interface ElectronAPI {
triggerMenuAction: (action: string) => void;
onExecuteAction: (callback: (action: string) => void) => void;
getPlatform: () => string;
getArch: () => string;
getHomeDir: () => Promise<string>;
createWebView: (id: string, url: string) => Promise<any>;
hideWebView: (id: string) => Promise<any>;