diff --git a/apps/desktop/src/main/lib/tmux-manager.ts b/apps/desktop/src/main/lib/tmux-manager.ts index d40b2736339..e32e3aab80f 100644 --- a/apps/desktop/src/main/lib/tmux-manager.ts +++ b/apps/desktop/src/main/lib/tmux-manager.ts @@ -183,7 +183,7 @@ class TmuxManager { ["status", "off"], ["set-titles", "off"], ["allow-rename", "off"], - ["mouse", "off"], + ["mouse", "on"], // Enable mouse mode for scroll handling ["focus-events", "on"], ["history-limit", "200000"], ["remain-on-exit", "off"], diff --git a/apps/desktop/src/main/lib/window-manager.ts b/apps/desktop/src/main/lib/window-manager.ts index 736cabd2991..1bdac755dad 100644 --- a/apps/desktop/src/main/lib/window-manager.ts +++ b/apps/desktop/src/main/lib/window-manager.ts @@ -7,9 +7,10 @@ class WindowManager { private windowWorkspaces: Map = new Map(); private restoredWindowIds: Set = new Set(); - async createWindow( - restoreState?: { workspaceId: string | null; bounds?: Electron.Rectangle }, - ): Promise { + async createWindow(restoreState?: { + workspaceId: string | null; + bounds?: Electron.Rectangle; + }): Promise { const window = await MainWindow(); // Restore window bounds if provided @@ -55,7 +56,7 @@ class WindowManager { // Save final state before closing (window is still valid here) // Get workspace ID from our map before window might be destroyed const workspaceId = this.windowWorkspaces.get(window) ?? null; - + try { if (!window.isDestroyed()) { const bounds = window.getBounds(); @@ -77,7 +78,10 @@ class WindowManager { } catch (error) { // Silently fail if window is destroyed - we'll clean up in closed handler if (!(error instanceof Error && error.message.includes("destroyed"))) { - console.error("[WindowManager] Failed to save window state on close:", error); + console.error( + "[WindowManager] Failed to save window state on close:", + error, + ); } } }); @@ -109,7 +113,10 @@ class WindowManager { return this.windowWorkspaces.get(window) ?? null; } - setWorkspaceForWindow(window: BrowserWindow, workspaceId: string | null): void { + setWorkspaceForWindow( + window: BrowserWindow, + workspaceId: string | null, + ): void { this.windowWorkspaces.set(window, workspaceId); // Persist the workspace association windowStateManager.saveWindowState(window, workspaceId); diff --git a/apps/desktop/src/main/lib/window-state-manager.ts b/apps/desktop/src/main/lib/window-state-manager.ts index bea74137411..e254ffacae8 100644 --- a/apps/desktop/src/main/lib/window-state-manager.ts +++ b/apps/desktop/src/main/lib/window-state-manager.ts @@ -132,7 +132,10 @@ class WindowStateManager { this.write(state); } catch (error) { - console.error("[WindowStateManager] Failed to save window state by ID:", error); + console.error( + "[WindowStateManager] Failed to save window state by ID:", + error, + ); } } @@ -157,4 +160,3 @@ class WindowStateManager { } export default WindowStateManager.getInstance(); - diff --git a/apps/desktop/src/main/lib/workspace-ipcs.ts b/apps/desktop/src/main/lib/workspace-ipcs.ts index 4f44ca803f8..87f9edc8aa0 100644 --- a/apps/desktop/src/main/lib/workspace-ipcs.ts +++ b/apps/desktop/src/main/lib/workspace-ipcs.ts @@ -10,9 +10,9 @@ import type { } from "shared/types"; import configManager from "./config-manager"; +import windowManager from "./window-manager"; import workspaceManager from "./workspace-manager"; import worktreeManager from "./worktree-manager"; -import windowManager from "./window-manager"; export function registerWorkspaceIPCs() { // Open repository dialog @@ -627,7 +627,9 @@ export function registerWorkspaceIPCs() { // Detect main branch instead of using workspace.branch // This ensures we compare against main/master, not a feature branch - const mainBranch = await worktreeManager.detectMainBranch(workspace.repoPath); + const mainBranch = await worktreeManager.detectMainBranch( + workspace.repoPath, + ); return await worktreeManager.getGitDiffFile( worktree.path, diff --git a/apps/desktop/src/main/lib/worktree-manager.ts b/apps/desktop/src/main/lib/worktree-manager.ts index 1df6166e1a3..9fcdb203cd9 100644 --- a/apps/desktop/src/main/lib/worktree-manager.ts +++ b/apps/desktop/src/main/lib/worktree-manager.ts @@ -854,7 +854,10 @@ class WorktreeManager { const oldPath = parts[2]; // For renamed files // Check if file should be excluded - if (this.shouldExcludeFile(filePath) || (oldPath && this.shouldExcludeFile(oldPath))) { + if ( + this.shouldExcludeFile(filePath) || + (oldPath && this.shouldExcludeFile(oldPath)) + ) { continue; } diff --git a/apps/desktop/src/renderer/contexts/AppProviders.tsx b/apps/desktop/src/renderer/contexts/AppProviders.tsx index fee2124f14f..e347d689441 100644 --- a/apps/desktop/src/renderer/contexts/AppProviders.tsx +++ b/apps/desktop/src/renderer/contexts/AppProviders.tsx @@ -1,11 +1,11 @@ import type React from "react"; import { useState } from "react"; import { - WorkspaceProvider, - TabProvider, SidebarProvider, - WorktreeOperationsProvider, + TabProvider, TaskProvider, + WorkspaceProvider, + WorktreeOperationsProvider, } from "./index"; interface AppProvidersProps { @@ -15,7 +15,9 @@ interface AppProvidersProps { export function AppProviders({ children }: AppProvidersProps) { // Tab selection state needs to be lifted to AppProviders level // so WorkspaceProvider can use it - const [selectedWorktreeId, setSelectedWorktreeId] = useState(null); + const [selectedWorktreeId, setSelectedWorktreeId] = useState( + null, + ); const [selectedTabId, setSelectedTabId] = useState(null); return ( @@ -38,4 +40,3 @@ export function AppProviders({ children }: AppProvidersProps) { ); } - diff --git a/apps/desktop/src/renderer/contexts/SidebarContext.tsx b/apps/desktop/src/renderer/contexts/SidebarContext.tsx index 087e9a92873..da81e60268d 100644 --- a/apps/desktop/src/renderer/contexts/SidebarContext.tsx +++ b/apps/desktop/src/renderer/contexts/SidebarContext.tsx @@ -38,4 +38,3 @@ export function useSidebarContext() { } return context; } - diff --git a/apps/desktop/src/renderer/contexts/TabContext.tsx b/apps/desktop/src/renderer/contexts/TabContext.tsx index 11f25deab64..943f422ebd4 100644 --- a/apps/desktop/src/renderer/contexts/TabContext.tsx +++ b/apps/desktop/src/renderer/contexts/TabContext.tsx @@ -1,5 +1,5 @@ -import { createContext, useContext } from "react"; import type React from "react"; +import { createContext, useContext } from "react"; import type { Tab, Worktree } from "shared/types"; import { useTabs } from "../screens/main/hooks"; import { useWorkspaceContext } from "./WorkspaceContext"; @@ -45,11 +45,7 @@ export function TabProvider({ setSelectedTabId, }); - return ( - - {children} - - ); + return {children}; } export function useTabContext() { @@ -59,4 +55,3 @@ export function useTabContext() { } return context; } - diff --git a/apps/desktop/src/renderer/contexts/TaskContext.tsx b/apps/desktop/src/renderer/contexts/TaskContext.tsx index 8b419f20fdd..38c7cb91b00 100644 --- a/apps/desktop/src/renderer/contexts/TaskContext.tsx +++ b/apps/desktop/src/renderer/contexts/TaskContext.tsx @@ -1,10 +1,10 @@ import type React from "react"; import { createContext, useContext } from "react"; import type { TaskStatus } from "../screens/main/components/Layout/StatusIndicator"; -import type { PendingWorktree, UITask } from "../screens/main/types"; import { useTasks } from "../screens/main/hooks"; -import { useWorkspaceContext } from "./WorkspaceContext"; +import type { PendingWorktree, UITask } from "../screens/main/types"; import { useTabContext } from "./TabContext"; +import { useWorkspaceContext } from "./WorkspaceContext"; import { useWorktreeOperationsContext } from "./WorktreeOperationsContext"; interface TaskContextValue { @@ -50,9 +50,7 @@ export function TaskProvider({ children }: TaskProviderProps) { }); return ( - - {children} - + {children} ); } @@ -63,4 +61,3 @@ export function useTaskContext() { } return context; } - diff --git a/apps/desktop/src/renderer/contexts/WorkspaceContext.tsx b/apps/desktop/src/renderer/contexts/WorkspaceContext.tsx index acc634b050d..e5372d72a75 100644 --- a/apps/desktop/src/renderer/contexts/WorkspaceContext.tsx +++ b/apps/desktop/src/renderer/contexts/WorkspaceContext.tsx @@ -4,53 +4,53 @@ import type { Workspace } from "shared/types"; import { useWorkspace } from "../screens/main/hooks"; interface WorkspaceContextValue { - workspaces: Workspace[] | null; - currentWorkspace: Workspace | null; - setCurrentWorkspace: React.Dispatch>; - setWorkspaces: React.Dispatch>; - loading: boolean; - error: string | null; - showWorkspaceSelection: boolean; - setShowWorkspaceSelection: React.Dispatch>; - loadAllWorkspaces: () => Promise; - handleWorkspaceSelect: (workspaceId: string) => Promise; - handleWorkspaceSelectFromModal: (workspaceId: string) => Promise; - handleCreateWorkspaceFromModal: () => Promise; + workspaces: Workspace[] | null; + currentWorkspace: Workspace | null; + setCurrentWorkspace: React.Dispatch>; + setWorkspaces: React.Dispatch>; + loading: boolean; + error: string | null; + showWorkspaceSelection: boolean; + setShowWorkspaceSelection: React.Dispatch>; + loadAllWorkspaces: () => Promise; + handleWorkspaceSelect: (workspaceId: string) => Promise; + handleWorkspaceSelectFromModal: (workspaceId: string) => Promise; + handleCreateWorkspaceFromModal: () => Promise; } const WorkspaceContext = createContext( - undefined, + undefined, ); interface WorkspaceProviderProps { - children: React.ReactNode; - setSelectedWorktreeId?: (id: string | null) => void; - setSelectedTabId?: (id: string | null) => void; + children: React.ReactNode; + setSelectedWorktreeId?: (id: string | null) => void; + setSelectedTabId?: (id: string | null) => void; } export function WorkspaceProvider({ - children, - setSelectedWorktreeId, - setSelectedTabId, + children, + setSelectedWorktreeId, + setSelectedTabId, }: WorkspaceProviderProps) { - const workspaceData = useWorkspace({ - setSelectedWorktreeId, - setSelectedTabId, - }); + const workspaceData = useWorkspace({ + setSelectedWorktreeId, + setSelectedTabId, + }); - return ( - - {children} - - ); + return ( + + {children} + + ); } export function useWorkspaceContext() { - const context = useContext(WorkspaceContext); - if (context === undefined) { - throw new Error( - "useWorkspaceContext must be used within a WorkspaceProvider", - ); - } - return context; + const context = useContext(WorkspaceContext); + if (context === undefined) { + throw new Error( + "useWorkspaceContext must be used within a WorkspaceProvider", + ); + } + return context; } diff --git a/apps/desktop/src/renderer/contexts/WorktreeOperationsContext.tsx b/apps/desktop/src/renderer/contexts/WorktreeOperationsContext.tsx index 99769fa6d29..778273fd63d 100644 --- a/apps/desktop/src/renderer/contexts/WorktreeOperationsContext.tsx +++ b/apps/desktop/src/renderer/contexts/WorktreeOperationsContext.tsx @@ -2,29 +2,40 @@ import type React from "react"; import { createContext, useContext } from "react"; import type { Worktree } from "shared/types"; import { useWorktrees } from "../screens/main/hooks"; -import { useWorkspaceContext } from "./WorkspaceContext"; import { useTabContext } from "./TabContext"; +import { useWorkspaceContext } from "./WorkspaceContext"; interface WorktreeOperationsContextValue { handleWorktreeCreated: () => Promise; - handleWorktreeCreatedWithResult: () => Promise<{ id: string; worktrees?: Worktree[] } | null>; + handleWorktreeCreatedWithResult: () => Promise<{ + id: string; + worktrees?: Worktree[]; + } | null>; handleUpdateWorktree: (worktreeId: string, updatedWorktree: Worktree) => void; handleCreatePR: (selectedWorktreeId: string | null) => Promise; handleMergePR: (selectedWorktreeId: string | null) => Promise; handleDeleteWorktree: (worktreeId: string) => Promise; } -const WorktreeOperationsContext = createContext( - undefined, -); +const WorktreeOperationsContext = createContext< + WorktreeOperationsContextValue | undefined +>(undefined); interface WorktreeOperationsProviderProps { children: React.ReactNode; } -export function WorktreeOperationsProvider({ children }: WorktreeOperationsProviderProps) { - const { currentWorkspace, setCurrentWorkspace, setWorkspaces, loadAllWorkspaces } = useWorkspaceContext(); - const { selectedWorktreeId, setSelectedWorktreeId, setSelectedTabId } = useTabContext(); +export function WorktreeOperationsProvider({ + children, +}: WorktreeOperationsProviderProps) { + const { + currentWorkspace, + setCurrentWorkspace, + setWorkspaces, + loadAllWorkspaces, + } = useWorkspaceContext(); + const { selectedWorktreeId, setSelectedWorktreeId, setSelectedTabId } = + useTabContext(); const worktreeOperations = useWorktrees({ currentWorkspace, @@ -46,8 +57,9 @@ export function WorktreeOperationsProvider({ children }: WorktreeOperationsProvi export function useWorktreeOperationsContext() { const context = useContext(WorktreeOperationsContext); if (context === undefined) { - throw new Error("useWorktreeOperationsContext must be used within a WorktreeOperationsProvider"); + throw new Error( + "useWorktreeOperationsContext must be used within a WorktreeOperationsProvider", + ); } return context; } - diff --git a/apps/desktop/src/renderer/contexts/index.ts b/apps/desktop/src/renderer/contexts/index.ts index 42bd7c03468..4ffe34ebbb6 100644 --- a/apps/desktop/src/renderer/contexts/index.ts +++ b/apps/desktop/src/renderer/contexts/index.ts @@ -1,8 +1,10 @@ export { AppProviders } from "./AppProviders"; -export { WorkspaceProvider, useWorkspaceContext } from "./WorkspaceContext"; -export { TabProvider, useTabContext } from "./TabContext"; export { SidebarProvider, useSidebarContext } from "./SidebarContext"; -export { WorktreeOperationsProvider, useWorktreeOperationsContext } from "./WorktreeOperationsContext"; +export { TabProvider, useTabContext } from "./TabContext"; export { TaskProvider, useTaskContext } from "./TaskContext"; -export { WorktreeProvider, useWorktree } from "./WorktreeContext"; - +export { useWorkspaceContext, WorkspaceProvider } from "./WorkspaceContext"; +export { useWorktree, WorktreeProvider } from "./WorktreeContext"; +export { + useWorktreeOperationsContext, + WorktreeOperationsProvider, +} from "./WorktreeOperationsContext"; diff --git a/apps/desktop/src/renderer/globals.css b/apps/desktop/src/renderer/globals.css index 8692abbe85d..7773f13c792 100644 --- a/apps/desktop/src/renderer/globals.css +++ b/apps/desktop/src/renderer/globals.css @@ -135,15 +135,24 @@ -webkit-font-smoothing: antialiased; } - /* Ensure xterm terminal fills container height */ + /* Fix xterm.js scrollbar positioning */ .xterm { - height: 100% !important; - width: 100% !important; + position: relative; + height: 100%; + width: 100%; } - .xterm-screen { - height: 100% !important; - width: 100% !important; + .xterm .xterm-viewport { + overflow-y: auto; + position: absolute; + right: 0; + left: 0; + top: 0; + bottom: 0; + } + + .xterm .xterm-screen { + position: relative; } /* Hide scrollbar for workspace carousel and tabs */ diff --git a/apps/desktop/src/renderer/screens/main/MainScreen.tsx b/apps/desktop/src/renderer/screens/main/MainScreen.tsx index fb15c6b9409..63ec8b16199 100644 --- a/apps/desktop/src/renderer/screens/main/MainScreen.tsx +++ b/apps/desktop/src/renderer/screens/main/MainScreen.tsx @@ -1,6 +1,13 @@ import { useState } from "react"; import { DndProvider } from "react-dnd"; import { HTML5Backend } from "react-dnd-html5-backend"; +import { + useSidebarContext, + useTabContext, + useTaskContext, + useWorkspaceContext, + useWorktreeOperationsContext, +} from "../../contexts"; import { AppFrame } from "./components/AppFrame"; import { Background } from "./components/Background"; import { AddTaskModal } from "./components/Layout/AddTaskModal"; @@ -8,13 +15,6 @@ import { TaskTabs } from "./components/Layout/TaskTabs"; import { MainContentArea } from "./components/MainContentArea"; import { SidebarOverlay } from "./components/SidebarOverlay"; import { WorkspaceSelectionModal } from "./components/WorkspaceSelectionModal"; -import { - useWorkspaceContext, - useTabContext, - useSidebarContext, - useWorktreeOperationsContext, - useTaskContext, -} from "../../contexts"; import type { AppMode } from "./types"; import { enrichWorktreesWithTasks } from "./utils"; diff --git a/apps/desktop/src/renderer/screens/main/components/AppFrame/AppFrame.tsx b/apps/desktop/src/renderer/screens/main/components/AppFrame/AppFrame.tsx index b31079f13bb..5bce819bb7c 100644 --- a/apps/desktop/src/renderer/screens/main/components/AppFrame/AppFrame.tsx +++ b/apps/desktop/src/renderer/screens/main/components/AppFrame/AppFrame.tsx @@ -3,9 +3,5 @@ interface AppFrameProps { } export function AppFrame({ children }: AppFrameProps) { - return ( -
- {children} -
- ); + return
{children}
; } diff --git a/apps/desktop/src/renderer/screens/main/components/DiffView/DiffContent.tsx b/apps/desktop/src/renderer/screens/main/components/DiffView/DiffContent.tsx index 83efbc0a4e9..16e81d0186e 100644 --- a/apps/desktop/src/renderer/screens/main/components/DiffView/DiffContent.tsx +++ b/apps/desktop/src/renderer/screens/main/components/DiffView/DiffContent.tsx @@ -155,14 +155,15 @@ export const DiffContent = memo(function DiffContent({ )} {file.status} diff --git a/apps/desktop/src/renderer/screens/main/components/DiffView/DiffContentArea.tsx b/apps/desktop/src/renderer/screens/main/components/DiffView/DiffContentArea.tsx index 56c41c6749f..678bba98b74 100644 --- a/apps/desktop/src/renderer/screens/main/components/DiffView/DiffContentArea.tsx +++ b/apps/desktop/src/renderer/screens/main/components/DiffView/DiffContentArea.tsx @@ -49,21 +49,31 @@ export function DiffContentArea({ // Load selected file immediately useEffect(() => { - if (selectedFile && loadFileContent && !loadedFiles.has(selectedFile) && !loadingFiles.has(selectedFile)) { + if ( + selectedFile && + loadFileContent && + !loadedFiles.has(selectedFile) && + !loadingFiles.has(selectedFile) + ) { loadFileContent(selectedFile); } }, [selectedFile, loadFileContent, loadedFiles, loadingFiles]); // Use intersection observer to load files when they come into view useEffect(() => { - if (viewMode !== "files" || !scrollContainerRef.current || !loadFileContent) return; + if (viewMode !== "files" || !scrollContainerRef.current || !loadFileContent) + return; const observer = new IntersectionObserver( (entries) => { for (const entry of entries) { if (entry.isIntersecting) { const fileId = entry.target.id.replace("file-diff-", ""); - if (fileId && !loadedFilesRef.current.has(fileId) && !loadingFilesRef.current.has(fileId)) { + if ( + fileId && + !loadedFilesRef.current.has(fileId) && + !loadingFilesRef.current.has(fileId) + ) { loadFileContentRef.current?.(fileId); } } @@ -361,4 +371,3 @@ export function DiffContentArea({ ); } - diff --git a/apps/desktop/src/renderer/screens/main/components/Layout/AddTaskModal.tsx b/apps/desktop/src/renderer/screens/main/components/Layout/AddTaskModal.tsx index bf3b1a4b5a1..09505c8dc0e 100644 --- a/apps/desktop/src/renderer/screens/main/components/Layout/AddTaskModal.tsx +++ b/apps/desktop/src/renderer/screens/main/components/Layout/AddTaskModal.tsx @@ -34,12 +34,12 @@ export const AddTaskModal: React.FC = ({ const [searchQuery, setSearchQuery] = useState(""); const [selectedTaskId, setSelectedTaskId] = useState(null); - const { tasks, isLoadingTasks, tasksError, refetch: refetchTasks } = useTaskData( - isOpen, - mode, - currentWorkspaceId ?? null, - worktrees, - ); + const { + tasks, + isLoadingTasks, + tasksError, + refetch: refetchTasks, + } = useTaskData(isOpen, mode, currentWorkspaceId ?? null, worktrees); const formState = useTaskForm(isOpen, mode, branches, worktrees); @@ -231,7 +231,7 @@ export const AddTaskModal: React.FC = ({ ) : ( <> {/* Show creating view when creating or when there's a status */} - {(isCreating || setupStatus) ? ( + {isCreating || setupStatus ? ( = ({ sourceBranch={formState.sourceBranch} onSourceBranchChange={formState.setSourceBranch} cloneTabsFromWorktreeId={formState.cloneTabsFromWorktreeId} - onCloneTabsFromWorktreeIdChange={formState.setCloneTabsFromWorktreeId} + onCloneTabsFromWorktreeIdChange={ + formState.setCloneTabsFromWorktreeId + } branches={branches} worktrees={worktrees} onSubmit={handleCreateTask} diff --git a/apps/desktop/src/renderer/screens/main/components/Layout/AddTaskModal/CreatingView.tsx b/apps/desktop/src/renderer/screens/main/components/Layout/AddTaskModal/CreatingView.tsx index 84d9f5a44a4..4f6e995b77a 100644 --- a/apps/desktop/src/renderer/screens/main/components/Layout/AddTaskModal/CreatingView.tsx +++ b/apps/desktop/src/renderer/screens/main/components/Layout/AddTaskModal/CreatingView.tsx @@ -1,7 +1,7 @@ import { Button } from "@superset/ui/button"; +import { ScrollArea } from "@superset/ui/scroll-area"; import { Loader2 } from "lucide-react"; import type React from "react"; -import { ScrollArea } from "@superset/ui/scroll-area"; import { TerminalOutput } from "../../Sidebar/components/CreateWorktreeModal/TerminalOutput"; interface CreatingViewProps { @@ -13,7 +13,7 @@ interface CreatingViewProps { function getStatusType(status?: string): "error" | "success" | "creating" { if (!status) return "creating"; - + const lowerStatus = status.toLowerCase(); if (lowerStatus.includes("failed") || lowerStatus.includes("error")) { return "error"; @@ -103,4 +103,3 @@ export const CreatingView: React.FC = ({ ); }; - diff --git a/apps/desktop/src/renderer/screens/main/components/Layout/AddTaskModal/TaskForm.tsx b/apps/desktop/src/renderer/screens/main/components/Layout/AddTaskModal/TaskForm.tsx index 07e46b6d640..845f469e5c5 100644 --- a/apps/desktop/src/renderer/screens/main/components/Layout/AddTaskModal/TaskForm.tsx +++ b/apps/desktop/src/renderer/screens/main/components/Layout/AddTaskModal/TaskForm.tsx @@ -80,7 +80,9 @@ export const TaskForm: React.FC = ({