mirror of
https://github.com/aaif-goose/goose.git
synced 2026-07-03 14:10:03 +02:00
fix: menu bar and dock icon settings (#2490)
Co-authored-by: Zane Staggs <zane@squareup.com>
This commit is contained in:
+12
-4
@@ -2,12 +2,20 @@
|
||||
|
||||
# Only auto-format desktop TS code if relevant files are modified
|
||||
if git diff --cached --name-only | grep -q "^ui/desktop/"; then
|
||||
. "$(dirname -- "$0")/_/husky.sh"
|
||||
cd ui/desktop && npx lint-staged
|
||||
if [ -d "ui/desktop" ]; then
|
||||
. "$(dirname -- "$0")/_/husky.sh"
|
||||
cd ui/desktop && npx lint-staged
|
||||
else
|
||||
echo "Warning: ui/desktop directory does not exist, skipping lint-staged"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Only auto-format ui-v2 TS code if relevant files are modified
|
||||
if git diff --cached --name-only | grep -q "^ui-v2/"; then
|
||||
. "$(dirname -- "$0")/_/husky.sh"
|
||||
cd ui-v2 && npx lint-staged
|
||||
if [ -d "ui-v2" ]; then
|
||||
. "$(dirname -- "$0")/_/husky.sh"
|
||||
cd ui-v2 && npx lint-staged
|
||||
else
|
||||
echo "Warning: ui-v2 directory does not exist, skipping lint-staged"
|
||||
fi
|
||||
fi
|
||||
|
||||
@@ -7,6 +7,7 @@ import { ModeSection } from './mode/ModeSection';
|
||||
import { ToolSelectionStrategySection } from './tool_selection_strategy/ToolSelectionStrategySection';
|
||||
import SessionSharingSection from './sessions/SessionSharingSection';
|
||||
import { ResponseStylesSection } from './response_styles/ResponseStylesSection';
|
||||
import AppSettingsSection from './app/AppSettingsSection';
|
||||
import { ExtensionConfig } from '../../api';
|
||||
import MoreMenuLayout from '../more_menu/MoreMenuLayout';
|
||||
|
||||
@@ -53,6 +54,8 @@ export default function SettingsView({
|
||||
<ResponseStylesSection />
|
||||
{/* Tool Selection Strategy */}
|
||||
<ToolSelectionStrategySection setView={setView} />
|
||||
{/* App Settings */}
|
||||
<AppSettingsSection />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,112 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import { Switch } from '../../ui/switch';
|
||||
|
||||
export default function AppSettingsSection() {
|
||||
const [menuBarIconEnabled, setMenuBarIconEnabled] = useState(true);
|
||||
const [dockIconEnabled, setDockIconEnabled] = useState(true);
|
||||
const [isMacOS, setIsMacOS] = useState(false);
|
||||
const [isDockSwitchDisabled, setIsDockSwitchDisabled] = useState(false);
|
||||
|
||||
// Check if running on macOS
|
||||
useEffect(() => {
|
||||
setIsMacOS(window.electron.platform === 'darwin');
|
||||
}, []);
|
||||
|
||||
// Load menu bar and dock icon states
|
||||
useEffect(() => {
|
||||
window.electron.getMenuBarIconState().then((enabled) => {
|
||||
setMenuBarIconEnabled(enabled);
|
||||
});
|
||||
|
||||
if (isMacOS) {
|
||||
window.electron.getDockIconState().then((enabled) => {
|
||||
setDockIconEnabled(enabled);
|
||||
});
|
||||
}
|
||||
}, [isMacOS]);
|
||||
|
||||
const handleMenuBarIconToggle = async () => {
|
||||
const newState = !menuBarIconEnabled;
|
||||
// If we're turning off the menu bar icon and the dock icon is hidden,
|
||||
// we need to show the dock icon to maintain accessibility
|
||||
if (!newState && !dockIconEnabled && isMacOS) {
|
||||
const success = await window.electron.setDockIcon(true);
|
||||
if (success) {
|
||||
setDockIconEnabled(true);
|
||||
}
|
||||
}
|
||||
const success = await window.electron.setMenuBarIcon(newState);
|
||||
if (success) {
|
||||
setMenuBarIconEnabled(newState);
|
||||
}
|
||||
};
|
||||
|
||||
const handleDockIconToggle = async () => {
|
||||
const newState = !dockIconEnabled;
|
||||
// If we're turning off the dock icon and the menu bar icon is hidden,
|
||||
// we need to show the menu bar icon to maintain accessibility
|
||||
if (!newState && !menuBarIconEnabled) {
|
||||
const success = await window.electron.setMenuBarIcon(true);
|
||||
if (success) {
|
||||
setMenuBarIconEnabled(true);
|
||||
}
|
||||
}
|
||||
|
||||
// Disable the switch to prevent rapid toggling
|
||||
setIsDockSwitchDisabled(true);
|
||||
setTimeout(() => {
|
||||
setIsDockSwitchDisabled(false);
|
||||
}, 1000);
|
||||
|
||||
// Set the dock icon state
|
||||
const success = await window.electron.setDockIcon(newState);
|
||||
if (success) {
|
||||
setDockIconEnabled(newState);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<section id="appSettings" className="px-8">
|
||||
<div className="flex justify-between items-center mb-2">
|
||||
<h2 className="text-xl font-medium text-textStandard">App Settings</h2>
|
||||
</div>
|
||||
<div className="pb-8">
|
||||
<p className="text-sm text-textStandard mb-6">Configure Goose app</p>
|
||||
<div>
|
||||
<div className="flex items-center justify-between mb-4">
|
||||
<div>
|
||||
<h3 className="text-textStandard">Menu Bar Icon</h3>
|
||||
<p className="text-xs text-textSubtle max-w-md mt-[2px]">
|
||||
Show Goose in the menu bar
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex items-center">
|
||||
<Switch
|
||||
checked={menuBarIconEnabled}
|
||||
onCheckedChange={handleMenuBarIconToggle}
|
||||
variant="mono"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{isMacOS && (
|
||||
<div className="flex items-center justify-between mb-4">
|
||||
<div>
|
||||
<h3 className="text-textStandard">Dock Icon</h3>
|
||||
<p className="text-xs text-textSubtle max-w-md mt-[2px]">Show Goose in the dock</p>
|
||||
</div>
|
||||
<div className="flex items-center">
|
||||
<Switch
|
||||
disabled={isDockSwitchDisabled}
|
||||
checked={dockIconEnabled}
|
||||
onCheckedChange={handleDockIconToggle}
|
||||
variant="mono"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
@@ -29,7 +29,7 @@ export const ResponseStylesSection = () => {
|
||||
<div className="flex justify-between items-center mb-2">
|
||||
<h2 className="text-xl font-medium text-textStandard">Response Styles</h2>
|
||||
</div>
|
||||
<div className="pb-8">
|
||||
<div className="border-b border-borderSubtle pb-8">
|
||||
<p className="text-sm text-textStandard mb-6">
|
||||
Choose how Goose should format and style its responses
|
||||
</p>
|
||||
|
||||
+88
-1
@@ -574,7 +574,17 @@ const createChat = async (
|
||||
// Track tray instance
|
||||
let tray: Tray | null = null;
|
||||
|
||||
const destroyTray = () => {
|
||||
if (tray) {
|
||||
tray.destroy();
|
||||
tray = null;
|
||||
}
|
||||
};
|
||||
|
||||
const createTray = () => {
|
||||
// If tray already exists, destroy it first
|
||||
destroyTray();
|
||||
|
||||
const isDev = process.env.NODE_ENV === 'development';
|
||||
let iconPath: string;
|
||||
|
||||
@@ -701,6 +711,73 @@ ipcMain.handle('directory-chooser', (_event, replace: boolean = false) => {
|
||||
return openDirectoryDialog(replace);
|
||||
});
|
||||
|
||||
// Handle menu bar icon visibility
|
||||
ipcMain.handle('set-menu-bar-icon', async (_event, show: boolean) => {
|
||||
try {
|
||||
const settings = loadSettings();
|
||||
settings.showMenuBarIcon = show;
|
||||
saveSettings(settings);
|
||||
|
||||
if (show) {
|
||||
createTray();
|
||||
} else {
|
||||
destroyTray();
|
||||
}
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error('Error setting menu bar icon:', error);
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.handle('get-menu-bar-icon-state', () => {
|
||||
try {
|
||||
const settings = loadSettings();
|
||||
return settings.showMenuBarIcon ?? true;
|
||||
} catch (error) {
|
||||
console.error('Error getting menu bar icon state:', error);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
// Handle dock icon visibility (macOS only)
|
||||
ipcMain.handle('set-dock-icon', async (_event, show: boolean) => {
|
||||
try {
|
||||
if (process.platform !== 'darwin') return false;
|
||||
|
||||
const settings = loadSettings();
|
||||
settings.showDockIcon = show;
|
||||
saveSettings(settings);
|
||||
|
||||
if (show) {
|
||||
await app.dock.show();
|
||||
} else {
|
||||
// Only hide the dock if we have a menu bar icon to maintain accessibility
|
||||
if (settings.showMenuBarIcon) {
|
||||
app.dock.hide();
|
||||
setTimeout(() => {
|
||||
focusWindow();
|
||||
}, 50);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.error('Error setting dock icon:', error);
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
ipcMain.handle('get-dock-icon-state', () => {
|
||||
try {
|
||||
if (process.platform !== 'darwin') return true;
|
||||
const settings = loadSettings();
|
||||
return settings.showDockIcon ?? true;
|
||||
} catch (error) {
|
||||
console.error('Error getting dock icon state:', error);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
// Add file/directory selection handler
|
||||
ipcMain.handle('select-file-or-directory', async () => {
|
||||
const result = await dialog.showOpenDialog({
|
||||
@@ -1122,10 +1199,20 @@ app.whenReady().then(async () => {
|
||||
}, 5000);
|
||||
}
|
||||
|
||||
// Create tray if enabled in settings
|
||||
const settings = loadSettings();
|
||||
if (settings.showMenuBarIcon) {
|
||||
createTray();
|
||||
}
|
||||
|
||||
// Handle dock icon visibility (macOS only)
|
||||
if (process.platform === 'darwin' && !settings.showDockIcon && settings.showMenuBarIcon) {
|
||||
app.dock.hide();
|
||||
}
|
||||
|
||||
// Parse command line arguments
|
||||
const { dirPath } = parseArgs();
|
||||
|
||||
createTray();
|
||||
createNewWindow(app, dirPath);
|
||||
|
||||
// Get the existing menu
|
||||
|
||||
@@ -58,6 +58,10 @@ type ElectronAPI = {
|
||||
writeFile: (directory: string, content: string) => Promise<boolean>;
|
||||
getAllowedExtensions: () => Promise<string[]>;
|
||||
getPathForFile: (file: File) => string;
|
||||
setMenuBarIcon: (show: boolean) => Promise<boolean>;
|
||||
getMenuBarIconState: () => Promise<boolean>;
|
||||
setDockIcon: (show: boolean) => Promise<boolean>;
|
||||
getDockIconState: () => Promise<boolean>;
|
||||
on: (
|
||||
channel: string,
|
||||
callback: (event: Electron.IpcRendererEvent, ...args: unknown[]) => void
|
||||
@@ -117,6 +121,10 @@ const electronAPI: ElectronAPI = {
|
||||
ipcRenderer.invoke('write-file', filePath, content),
|
||||
getPathForFile: (file: File) => webUtils.getPathForFile(file),
|
||||
getAllowedExtensions: () => ipcRenderer.invoke('get-allowed-extensions'),
|
||||
setMenuBarIcon: (show: boolean) => ipcRenderer.invoke('set-menu-bar-icon', show),
|
||||
getMenuBarIconState: () => ipcRenderer.invoke('get-menu-bar-icon-state'),
|
||||
setDockIcon: (show: boolean) => ipcRenderer.invoke('set-dock-icon', show),
|
||||
getDockIconState: () => ipcRenderer.invoke('get-dock-icon-state'),
|
||||
on: (
|
||||
channel: string,
|
||||
callback: (event: Electron.IpcRendererEvent, ...args: unknown[]) => void
|
||||
|
||||
@@ -10,6 +10,8 @@ export interface EnvToggles {
|
||||
|
||||
export interface Settings {
|
||||
envToggles: EnvToggles;
|
||||
showMenuBarIcon: boolean;
|
||||
showDockIcon: boolean;
|
||||
}
|
||||
|
||||
// Constants
|
||||
@@ -20,6 +22,8 @@ const defaultSettings: Settings = {
|
||||
GOOSE_SERVER__MEMORY: false,
|
||||
GOOSE_SERVER__COMPUTER_CONTROLLER: false,
|
||||
},
|
||||
showMenuBarIcon: true,
|
||||
showDockIcon: true,
|
||||
};
|
||||
|
||||
// Settings management
|
||||
|
||||
Reference in New Issue
Block a user