import React from 'react'; import { SortableTabsStrip } from '@/components/ui/sortable-tabs-strip'; import { ProjectNotesTodoPanel } from '@/components/session/ProjectNotesTodoPanel'; import { GitView } from '@/components/views/GitView'; import { Icon } from "@/components/icon/Icon"; import { useGitStore } from '@/stores/useGitStore'; import { useProjectsStore } from '@/stores/useProjectsStore'; import { useDirectoryStore } from '@/stores/useDirectoryStore'; import { useUIStore } from '@/stores/useUIStore'; import { useRuntimeAPIs } from '@/hooks/useRuntimeAPIs'; import { useEffectiveDirectory } from '@/hooks/useEffectiveDirectory'; import { formatDirectoryName, cn } from '@/lib/utils'; import { useI18n } from '@/lib/i18n'; import { SidebarFilesTree } from './SidebarFilesTree'; type RightTab = 'git' | 'files' | 'context'; const isRightTab = (value: string): value is RightTab => value === 'git' || value === 'files' || value === 'context'; const RIGHT_TAB_FALLBACK: RightTab = 'files'; const isBrowserActive = (): boolean => { if (typeof document !== 'undefined' && document.hidden) return false; if (typeof navigator !== 'undefined' && !navigator.onLine) return false; return true; }; /** * Keeps git status fresh while the right sidebar's Git tab is the visible * consumer. Replaces the GitPollingProvider removed in commit b2d5ccb4. * * Gating rules (mirror the right-sidebar render policy): * - sidebar must be open * - right tab must be 'git' (otherwise GitView is not the visible consumer) * - main tab must not be 'git' (otherwise secondaryView's GitView handles * refresh and this poll would duplicate work) * - browser must be visible + online * * Any condition flip resets the interval so the next tick starts fresh. */ function useRightSidebarGitSync( directory: string | undefined, isSidebarOpen: boolean, rightTab: RightTab | undefined, mainTab: string | undefined ) { const { git } = useRuntimeAPIs(); const ensureStatus = useGitStore((state) => state.ensureStatus); const shouldPoll = Boolean( directory && git && isSidebarOpen && rightTab === 'git' && mainTab !== 'git' ); React.useEffect(() => { if (!shouldPoll || !directory || !git) return; void ensureStatus(directory, git); const POLL_INTERVAL = 10_000; const id = window.setInterval(() => { if (!isBrowserActive()) return; void ensureStatus(directory, git); }, POLL_INTERVAL); return () => window.clearInterval(id); }, [shouldPoll, directory, git, ensureStatus]); } export const ProjectContextPanel: React.FC = () => { const activeProjectId = useProjectsStore((state) => state.activeProjectId); const projects = useProjectsStore((state) => state.projects); const homeDirectory = useDirectoryStore((state) => state.homeDirectory); const gitDirectories = useGitStore((state) => state.directories); const activeProject = React.useMemo(() => { if (activeProjectId) { return projects.find((project) => project.id === activeProjectId) ?? projects[0] ?? null; } return projects[0] ?? null; }, [activeProjectId, projects]); const projectRef = React.useMemo(() => { if (!activeProject) { return null; } return { id: activeProject.id, path: activeProject.path, }; }, [activeProject]); const projectLabel = React.useMemo(() => { if (!activeProject) { return null; } return activeProject.label?.trim() || formatDirectoryName(activeProject.path, homeDirectory) || activeProject.path; }, [activeProject, homeDirectory]); const canCreateWorktree = React.useMemo(() => { if (!activeProject) { return false; } return gitDirectories.get(activeProject.path)?.isGitRepo === true; }, [activeProject, gitDirectories]); return (