Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions src/main/app/window.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,15 @@ export function createMainWindow(): BrowserWindow {
mainWindow = null;
});

// Notify renderer when entering/leaving native fullscreen (for macOS titlebar padding)
mainWindow.on('enter-full-screen', () => {
mainWindow?.webContents.send('window:fullscreen-changed', true);
});

mainWindow.on('leave-full-screen', () => {
mainWindow?.webContents.send('window:fullscreen-changed', false);
});

return mainWindow;
}

Expand Down
2 changes: 2 additions & 0 deletions src/main/ipc/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { registerPtyIpc } from '../services/ptyIpc';
import { registerWorktreeIpc } from '../services/worktreeIpc';
import { registerFsIpc } from '../services/fsIpc';
import { registerWindowIpc } from '../services/windowIpc';

import { registerAppIpc } from './appIpc';
import { registerProjectIpc } from './projectIpc';
Expand Down Expand Up @@ -29,6 +30,7 @@ export function registerAllIpc() {
registerTelemetryIpc();
registerUpdateIpc();
registerSettingsIpc();
registerWindowIpc();

// Domain IPC
registerProjectIpc();
Expand Down
11 changes: 11 additions & 0 deletions src/main/preload.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,14 @@ contextBridge.exposeInMainWorld('electronAPI', {
getAppVersion: () => ipcRenderer.invoke('app:getAppVersion'),
getElectronVersion: () => ipcRenderer.invoke('app:getElectronVersion'),
getPlatform: () => ipcRenderer.invoke('app:getPlatform'),
// Window state
getFullScreenState: () => ipcRenderer.invoke('window:get-fullscreen-state'),
onFullScreenChange: (callback: (isFullScreen: boolean) => void) => {
const listener = (_: Electron.IpcRendererEvent, isFullScreen: boolean) =>
callback(isFullScreen);
ipcRenderer.on('window:fullscreen-changed', listener);
return () => ipcRenderer.removeListener('window:fullscreen-changed', listener);
},
// Updater
checkForUpdates: () => ipcRenderer.invoke('update:check'),
downloadUpdate: () => ipcRenderer.invoke('update:download'),
Expand Down Expand Up @@ -398,6 +406,9 @@ export interface ElectronAPI {
// App info
getVersion: () => Promise<string>;
getPlatform: () => Promise<string>;
// Window state
getFullScreenState: () => Promise<boolean>;
onFullScreenChange: (callback: (isFullScreen: boolean) => void) => () => void;
// Updater
checkForUpdates: () => Promise<{ success: boolean; result?: any; error?: string }>;
downloadUpdate: () => Promise<{ success: boolean; error?: string }>;
Expand Down
10 changes: 10 additions & 0 deletions src/main/services/windowIpc.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { ipcMain } from 'electron';
import { getMainWindow } from '../app/window';

export function registerWindowIpc(): void {
// Get the current fullscreen state of the main window
ipcMain.handle('window:get-fullscreen-state', () => {
const mainWindow = getMainWindow();
return mainWindow?.isFullScreen() || false;
});
}
73 changes: 61 additions & 12 deletions src/renderer/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ import {
normalizePathForComparison,
withRepoKey,
} from './lib/projectUtils';
import { isLinux, isMac, isWindows } from './lib/platform';
import { BrowserProvider } from './providers/BrowserProvider';
import { terminalSessionRegistry } from './terminal/SessionRegistry';
import { type Provider } from './types';
Expand Down Expand Up @@ -115,6 +116,7 @@ const AppContent: React.FC = () => {
const { toast } = useToast();
const [_, setVersion] = useState<string>('');
const [platform, setPlatform] = useState<string>('');
const [isFullscreen, setIsFullscreen] = useState<boolean>(false);
const {
installed: ghInstalled,
authenticated: isAuthenticated,
Expand All @@ -136,6 +138,7 @@ const AppContent: React.FC = () => {
const [activeTask, setActiveTask] = useState<Task | null>(null);
const [activeTaskProvider, setActiveTaskProvider] = useState<Provider | null>(null);
const [showSettings, setShowSettings] = useState<boolean>(false);
const [showFeedback, setShowFeedback] = useState<boolean>(false);
const [showCommandPalette, setShowCommandPalette] = useState<boolean>(false);
const [showFirstLaunchModal, setShowFirstLaunchModal] = useState<boolean>(false);
const deletingTaskIdsRef = useRef<Set<string>>(new Set());
Expand Down Expand Up @@ -281,6 +284,17 @@ const AppContent: React.FC = () => {
setShowSettings(false);
}, []);

const handleOpenFeedback = useCallback(() => {
void import('./lib/telemetryClient').then(({ captureTelemetry }) => {
captureTelemetry('toolbar_feedback_clicked');
});
setShowFeedback(true);
}, []);

const handleCloseFeedback = useCallback(() => {
setShowFeedback(false);
}, []);

const handleToggleCommandPalette = useCallback(() => {
setShowCommandPalette((prev) => !prev);
}, []);
Expand Down Expand Up @@ -429,6 +443,30 @@ const AppContent: React.FC = () => {
} catch {}
};

useEffect(() => {
let isMounted = true;
const loadFullscreenState = async () => {
try {
const fullscreen = await window.electronAPI.getFullScreenState();
if (isMounted) {
setIsFullscreen(fullscreen);
}
} catch {}
};

loadFullscreenState();
const cleanup = window.electronAPI.onFullScreenChange((fullscreen) => {
if (isMounted) {
setIsFullscreen(fullscreen);
}
});

return () => {
isMounted = false;
cleanup();
};
}, []);

useEffect(() => {
const loadAppData = async () => {
try {
Expand Down Expand Up @@ -540,12 +578,11 @@ const AppContent: React.FC = () => {
log.error('Failed to save project:', saveResult.error);
}
} else {
const updateHint =
platform === 'darwin'
? 'Tip: Update GitHub CLI with: brew upgrade gh — then restart Emdash.'
: platform === 'win32'
? 'Tip: Update GitHub CLI with: winget upgrade GitHub.cli — then restart Emdash.'
: 'Tip: Update GitHub CLI via your package manager (e.g., apt/dnf) and restart Emdash.';
const updateHint = isMac(platform)
? 'Tip: Update GitHub CLI with: brew upgrade gh — then restart Emdash.'
: isWindows(platform)
? 'Tip: Update GitHub CLI with: winget upgrade GitHub.cli — then restart Emdash.'
: 'Tip: Update GitHub CLI via your package manager (e.g., apt/dnf) and restart Emdash.';
toast({
title: 'GitHub Connection Failed',
description: `Git repository detected but couldn't connect to GitHub: ${githubInfo.error}\n\n${updateHint}`,
Expand Down Expand Up @@ -917,11 +954,11 @@ const AppContent: React.FC = () => {
if (!cliInstalled) {
// Detect platform for better messaging
let installMessage = 'Installing GitHub CLI...';
if (platform === 'darwin') {
if (isMac(platform)) {
installMessage = 'Installing GitHub CLI via Homebrew...';
} else if (platform === 'linux') {
} else if (isLinux(platform)) {
installMessage = 'Installing GitHub CLI via apt...';
} else if (platform === 'win32') {
} else if (isWindows(platform)) {
installMessage = 'Installing GitHub CLI via winget...';
}

Expand Down Expand Up @@ -1937,14 +1974,13 @@ const AppContent: React.FC = () => {
handleNextTask={handleNextTask}
handlePrevTask={handlePrevTask}
handleNewTask={handleNewTask}
handleOpenFeedback={handleOpenFeedback}
/>
<RightSidebarBridge
onCollapsedChange={handleRightSidebarCollapsedChange}
setCollapsedRef={rightSidebarSetCollapsedRef}
/>
<Titlebar
onToggleSettings={handleToggleSettings}
isSettingsOpen={showSettings}
currentPath={
activeTask?.metadata?.multiAgent?.enabled
? null
Expand All @@ -1957,10 +1993,11 @@ const AppContent: React.FC = () => {
taskPath={activeTask?.path || null}
projectPath={selectedProject?.path || null}
isTaskMultiAgent={Boolean(activeTask?.metadata?.multiAgent?.enabled)}
githubUser={user}
onToggleKanban={handleToggleKanban}
isKanbanOpen={Boolean(showKanban)}
kanbanAvailable={Boolean(selectedProject)}
platform={platform}
isFullscreen={isFullscreen}
/>
<div className="flex flex-1 overflow-hidden pt-[var(--tb)]">
<ResizablePanelGroup
Expand Down Expand Up @@ -1990,12 +2027,24 @@ const AppContent: React.FC = () => {
activeTask={activeTask || undefined}
onReorderProjects={handleReorderProjects}
onReorderProjectsFull={handleReorderProjectsFull}
githubInstalled={ghInstalled}
githubAuthenticated={isAuthenticated}
githubUser={user}
onGithubConnect={handleGithubConnect}
githubLoading={githubLoading}
githubStatusMessage={githubStatusMessage}
githubInitialized={isGithubInitialized}
onSidebarContextChange={handleSidebarContextChange}
onCreateTaskForProject={handleStartCreateTaskFromSidebar}
isCreatingTask={isCreatingTask}
onDeleteTask={handleDeleteTask}
onDeleteProject={handleDeleteProject}
isHomeView={showHomeView}
onToggleSettings={handleToggleSettings}
isSettingsOpen={showSettings}
isFeedbackOpen={showFeedback}
onOpenFeedback={handleOpenFeedback}
onCloseFeedback={handleCloseFeedback}
/>
</ResizablePanel>
<ResizableHandle
Expand Down
3 changes: 3 additions & 0 deletions src/renderer/components/AppKeyboardShortcuts.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export interface AppKeyboardShortcutsProps {
handleNextTask: () => void;
handlePrevTask: () => void;
handleNewTask: () => void;
handleOpenFeedback?: () => void;
}

const AppKeyboardShortcuts: React.FC<AppKeyboardShortcutsProps> = ({
Expand All @@ -29,6 +30,7 @@ const AppKeyboardShortcuts: React.FC<AppKeyboardShortcutsProps> = ({
handleNextTask,
handlePrevTask,
handleNewTask,
handleOpenFeedback,
}) => {
const { toggle: toggleLeftSidebar } = useSidebar();
const { toggle: toggleRightSidebar } = useRightSidebar();
Expand All @@ -45,6 +47,7 @@ const AppKeyboardShortcuts: React.FC<AppKeyboardShortcutsProps> = ({
onNextProject: handleNextTask,
onPrevProject: handlePrevTask,
onNewTask: handleNewTask,
onOpenFeedback: handleOpenFeedback,
onCloseModal: showCommandPalette
? handleCloseCommandPalette
: showSettings
Expand Down
3 changes: 1 addition & 2 deletions src/renderer/components/CommandPalette.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {
Palette,
} from 'lucide-react';
import { APP_SHORTCUTS } from '../hooks/useKeyboardShortcuts';
import type { ShortcutModifier } from '../types/shortcuts';

interface CommandPaletteProps {
isOpen: boolean;
Expand All @@ -42,8 +43,6 @@ interface CommandPaletteProps {
onOpenProject?: () => void;
}

type ShortcutModifier = 'cmd' | 'ctrl' | 'shift' | 'alt' | 'option';

type CommandItem = {
id: string;
label: string;
Expand Down
Loading