diff --git a/.husky/pre-commit b/.husky/pre-commit
index f2796dcae8..4437aea846 100755
--- a/.husky/pre-commit
+++ b/.husky/pre-commit
@@ -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
diff --git a/ui/desktop/src/components/settings_v2/SettingsView.tsx b/ui/desktop/src/components/settings_v2/SettingsView.tsx
index b6a5bd7d3d..54d95b9227 100644
--- a/ui/desktop/src/components/settings_v2/SettingsView.tsx
+++ b/ui/desktop/src/components/settings_v2/SettingsView.tsx
@@ -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({
Configure Goose app
+ Show Goose in the menu bar
+ Show Goose in the dockApp Settings
+ Menu Bar Icon
+ Dock Icon
+
Choose how Goose should format and style its responses
diff --git a/ui/desktop/src/main.ts b/ui/desktop/src/main.ts index b93ee16f50..18387424a3 100644 --- a/ui/desktop/src/main.ts +++ b/ui/desktop/src/main.ts @@ -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 diff --git a/ui/desktop/src/preload.ts b/ui/desktop/src/preload.ts index 9f667aad08..13674f8827 100644 --- a/ui/desktop/src/preload.ts +++ b/ui/desktop/src/preload.ts @@ -58,6 +58,10 @@ type ElectronAPI = { writeFile: (directory: string, content: string) => Promise