import React from 'react'; import { IconCheck, IconChevronDown, IconFolder, IconHome, IconPin, IconPinned, IconPlus, IconDownload, IconSettings, IconMinus, IconSquare, IconX, IconCopy } from '@tabler/icons'; import { forwardRef, useCallback, useEffect, useMemo, useState } from 'react'; import toast from 'react-hot-toast'; import { useDispatch, useSelector } from 'react-redux'; import { savePreferences, showHomePage, showManageWorkspacePage, toggleSidebarCollapse } from 'providers/ReduxStore/slices/app'; import { closeConsole, openConsole } from 'providers/ReduxStore/slices/logs'; import { openWorkspaceDialog, switchWorkspace } from 'providers/ReduxStore/slices/workspaces/actions'; import { sortWorkspaces, toggleWorkspacePin } from 'utils/workspaces'; import Bruno from 'components/Bruno'; import MenuDropdown from 'ui/MenuDropdown'; import ActionIcon from 'ui/ActionIcon'; import IconSidebarToggle from 'components/Icons/IconSidebarToggle'; import CreateWorkspace from 'components/WorkspaceSidebar/CreateWorkspace'; import ImportWorkspace from 'components/WorkspaceSidebar/ImportWorkspace'; import IconBottombarToggle from 'components/Icons/IconBottombarToggle/index'; import StyledWrapper from './StyledWrapper'; import ResponseLayoutToggle from 'components/ResponsePane/ResponseLayoutToggle'; import { isMacOS, isWindowsOS, isLinuxOS } from 'utils/common/platform'; import classNames from 'classnames'; const getOsClass = () => { if (isMacOS()) return 'os-mac'; if (isWindowsOS()) return 'os-windows'; if (isLinuxOS()) return 'os-linux'; return 'os-other'; }; // Helper to get display name for workspace export const getWorkspaceDisplayName = (name) => { if (!name) return 'Untitled Workspace'; return name; }; const AppTitleBar = () => { const dispatch = useDispatch(); const [isFullScreen, setIsFullScreen] = useState(false); const [isMaximized, setIsMaximized] = useState(false); const osClass = getOsClass(); const isWindows = osClass === 'os-windows'; const isLinux = osClass === 'os-linux'; const showWindowControls = isWindows || isLinux; // Listen for fullscreen changes useEffect(() => { const { ipcRenderer } = window; if (!ipcRenderer) return; const removeEnterFullScreenListener = ipcRenderer.on('main:enter-full-screen', () => { setIsFullScreen(true); }); const removeLeaveFullScreenListener = ipcRenderer.on('main:leave-full-screen', () => { setIsFullScreen(false); }); return () => { removeEnterFullScreenListener(); removeLeaveFullScreenListener(); }; }, []); useEffect(() => { if (!showWindowControls) return; const { ipcRenderer } = window; if (!ipcRenderer) return; ipcRenderer.invoke('renderer:window-is-maximized') .then((maximized) => { setIsMaximized(maximized); }) .catch((error) => { console.error('Error getting initial maximized state:', error); }); const removeMaximizedListener = ipcRenderer.on('main:window-maximized', () => { setIsMaximized(true); }); const removeUnmaximizedListener = ipcRenderer.on('main:window-unmaximized', () => { setIsMaximized(false); }); return () => { removeMaximizedListener(); removeUnmaximizedListener(); }; }, [showWindowControls]); const handleMinimize = useCallback(() => { window.ipcRenderer?.send('renderer:window-minimize'); }, []); const handleMaximize = useCallback(() => { window.ipcRenderer?.send('renderer:window-maximize'); // State will be updated via IPC events from main process (main:window-maximized/main:window-unmaximized) }, []); const handleClose = useCallback(() => { window.ipcRenderer?.send('renderer:window-close'); }, []); // Get workspace info const { workspaces, activeWorkspaceUid } = useSelector((state) => state.workspaces); const preferences = useSelector((state) => state.app.preferences); const sidebarCollapsed = useSelector((state) => state.app.sidebarCollapsed); const isConsoleOpen = useSelector((state) => state.logs.isConsoleOpen); const activeWorkspace = workspaces.find((w) => w.uid === activeWorkspaceUid); // Sort workspaces according to preferences const sortedWorkspaces = useMemo(() => { return sortWorkspaces(workspaces, preferences); }, [workspaces, preferences]); const [createWorkspaceModalOpen, setCreateWorkspaceModalOpen] = useState(false); const [importWorkspaceModalOpen, setImportWorkspaceModalOpen] = useState(false); const WorkspaceName = forwardRef((props, ref) => { return (
{getWorkspaceDisplayName(activeWorkspace?.name)}
); }); const handleHomeClick = () => { dispatch(showHomePage()); }; const handleWorkspaceSwitch = (workspaceUid) => { dispatch(switchWorkspace(workspaceUid)); toast.success(`Switched to ${getWorkspaceDisplayName(workspaces.find((w) => w.uid === workspaceUid)?.name)}`); }; const handleOpenWorkspace = async () => { try { await dispatch(openWorkspaceDialog()); toast.success('Workspace opened successfully'); } catch (error) { toast.error(error.message || 'Failed to open workspace'); } }; const handleCreateWorkspace = () => { setCreateWorkspaceModalOpen(true); }; const handleManageWorkspaces = () => { dispatch(showManageWorkspacePage()); }; const handleImportWorkspace = () => { setImportWorkspaceModalOpen(true); }; const handlePinWorkspace = useCallback((workspaceUid, e) => { e.preventDefault(); e.stopPropagation(); const newPreferences = toggleWorkspacePin(workspaceUid, preferences); dispatch(savePreferences(newPreferences)); }, [dispatch, preferences]); const handleToggleSidebar = () => { dispatch(toggleSidebarCollapse()); }; const handleToggleDevtools = () => { if (isConsoleOpen) { dispatch(closeConsole()); } else { dispatch(openConsole()); } }; // Build workspace menu items const workspaceMenuItems = useMemo(() => { const items = sortedWorkspaces.map((workspace) => { const isActive = workspace.uid === activeWorkspaceUid; const isPinned = preferences?.workspaces?.pinnedWorkspaceUids?.includes(workspace.uid); return { id: workspace.uid, label: getWorkspaceDisplayName(workspace.name), onClick: () => handleWorkspaceSwitch(workspace.uid), className: `workspace-item ${isActive ? 'active' : ''}`, rightSection: (
{workspace.type !== 'default' && ( handlePinWorkspace(workspace.uid, e)} label={isPinned ? 'Unpin workspace' : 'Pin workspace'} size="sm" > {isPinned ? : } )} {isActive && }
) }; }); // Add label and action items items.push( { type: 'label', label: 'Workspaces' }, { id: 'create-workspace', leftSection: IconPlus, label: 'Create workspace', onClick: handleCreateWorkspace }, { id: 'open-workspace', leftSection: IconFolder, label: 'Open workspace', onClick: handleOpenWorkspace }, { id: 'import-workspace', leftSection: IconDownload, label: 'Import workspace', onClick: handleImportWorkspace }, { id: 'manage-workspaces', leftSection: IconSettings, label: 'Manage workspaces', onClick: handleManageWorkspaces } ); return items; }, [sortedWorkspaces, activeWorkspaceUid, preferences, handlePinWorkspace]); return ( {createWorkspaceModalOpen && ( setCreateWorkspaceModalOpen(false)} /> )} {importWorkspaceModalOpen && ( setImportWorkspaceModalOpen(false)} /> )}
{/* Left section: Home + Workspace */}
{/* Workspace Dropdown */}
{/* Center section: Bruno logo + text */}
Bruno
{/* Right section: Action buttons */}
{/* Toggle sidebar */} {/* Toggle devtools */}
{showWindowControls && (
)}
); }; export default AppTitleBar;