2026-02-16 14:15:19 +02:00
|
|
|
import React from 'react';
|
|
|
|
|
|
2026-03-02 02:11:33 +02:00
|
|
|
import { SortableTabsStrip } from '@/components/ui/sortable-tabs-strip';
|
2026-04-18 13:46:57 +03:00
|
|
|
import { ProjectNotesTodoPanel } from '@/components/session/ProjectNotesTodoPanel';
|
2026-05-06 02:43:13 +03:00
|
|
|
import { GitView } from '@/components/views/GitView';
|
2026-05-13 13:26:15 +03:00
|
|
|
import { Icon } from "@/components/icon/Icon";
|
2026-04-12 04:29:35 +08:00
|
|
|
import { useGitStore } from '@/stores/useGitStore';
|
2026-04-18 13:46:57 +03:00
|
|
|
import { useProjectsStore } from '@/stores/useProjectsStore';
|
|
|
|
|
import { useDirectoryStore } from '@/stores/useDirectoryStore';
|
|
|
|
|
import { useUIStore } from '@/stores/useUIStore';
|
2026-04-12 04:29:35 +08:00
|
|
|
import { useRuntimeAPIs } from '@/hooks/useRuntimeAPIs';
|
|
|
|
|
import { useEffectiveDirectory } from '@/hooks/useEffectiveDirectory';
|
2026-04-18 13:46:57 +03:00
|
|
|
import { formatDirectoryName } from '@/lib/utils';
|
2026-04-26 14:03:39 +03:00
|
|
|
import { useI18n } from '@/lib/i18n';
|
2026-02-16 14:15:19 +02:00
|
|
|
import { SidebarFilesTree } from './SidebarFilesTree';
|
|
|
|
|
|
2026-04-18 13:46:57 +03:00
|
|
|
type RightTab = 'git' | 'files' | 'context';
|
2026-02-16 14:15:19 +02:00
|
|
|
|
2026-04-12 04:29:35 +08:00
|
|
|
/**
|
|
|
|
|
* Keeps git status fresh while the right sidebar is open.
|
|
|
|
|
* Replaces the GitPollingProvider removed in commit b2d5ccb4.
|
|
|
|
|
* The previous polling ran globally; now we only refresh when the sidebar is open.
|
|
|
|
|
*/
|
|
|
|
|
function useRightSidebarGitSync(directory: string | undefined, isSidebarOpen: boolean) {
|
|
|
|
|
const { git } = useRuntimeAPIs();
|
|
|
|
|
const ensureStatus = useGitStore((state) => state.ensureStatus);
|
|
|
|
|
|
|
|
|
|
React.useEffect(() => {
|
|
|
|
|
if (!directory || !git || !isSidebarOpen) return;
|
|
|
|
|
|
|
|
|
|
void ensureStatus(directory, git);
|
|
|
|
|
|
|
|
|
|
const POLL_INTERVAL = 10_000;
|
|
|
|
|
const id = setInterval(() => {
|
|
|
|
|
if (typeof document !== 'undefined' && document.hidden) return;
|
|
|
|
|
void ensureStatus(directory, git);
|
|
|
|
|
}, POLL_INTERVAL);
|
|
|
|
|
|
|
|
|
|
return () => clearInterval(id);
|
|
|
|
|
}, [directory, git, isSidebarOpen, ensureStatus]);
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-18 13:46:57 +03:00
|
|
|
const ContextSidebarPanel: 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 (
|
|
|
|
|
<div className="h-full min-h-0 overflow-auto bg-sidebar">
|
|
|
|
|
<ProjectNotesTodoPanel
|
|
|
|
|
projectRef={projectRef}
|
|
|
|
|
projectLabel={projectLabel}
|
|
|
|
|
canCreateWorktree={canCreateWorktree}
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
};
|
|
|
|
|
|
2026-02-16 14:15:19 +02:00
|
|
|
export const RightSidebarTabs: React.FC = () => {
|
2026-04-26 14:03:39 +03:00
|
|
|
const { t } = useI18n();
|
2026-02-16 14:15:19 +02:00
|
|
|
const rightSidebarTab = useUIStore((state) => state.rightSidebarTab);
|
|
|
|
|
const setRightSidebarTab = useUIStore((state) => state.setRightSidebarTab);
|
2026-04-12 04:29:35 +08:00
|
|
|
const isRightSidebarOpen = useUIStore((state) => state.isRightSidebarOpen);
|
|
|
|
|
const directory = useEffectiveDirectory();
|
|
|
|
|
|
|
|
|
|
useRightSidebarGitSync(directory, isRightSidebarOpen);
|
2026-02-16 14:15:19 +02:00
|
|
|
|
2026-03-02 02:11:33 +02:00
|
|
|
const tabItems = React.useMemo(() => [
|
|
|
|
|
{
|
|
|
|
|
id: 'git',
|
2026-04-26 14:03:39 +03:00
|
|
|
label: t('layout.rightSidebar.git'),
|
2026-05-13 13:26:15 +03:00
|
|
|
icon: <Icon name="git-branch" className="h-3.5 w-3.5" />,
|
2026-03-02 02:11:33 +02:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
id: 'files',
|
2026-04-26 14:03:39 +03:00
|
|
|
label: t('layout.rightSidebar.files'),
|
2026-05-13 13:26:15 +03:00
|
|
|
icon: <Icon name="folder-3" className="h-3.5 w-3.5" />,
|
2026-03-02 02:11:33 +02:00
|
|
|
},
|
2026-04-18 13:46:57 +03:00
|
|
|
{
|
|
|
|
|
id: 'context',
|
2026-04-26 14:03:39 +03:00
|
|
|
label: t('layout.rightSidebar.context'),
|
2026-05-13 13:26:15 +03:00
|
|
|
icon: <Icon name="booklet" className="h-3.5 w-3.5" />,
|
2026-04-18 13:46:57 +03:00
|
|
|
},
|
2026-04-26 14:03:39 +03:00
|
|
|
], [t]);
|
2026-03-02 02:11:33 +02:00
|
|
|
|
2026-02-16 14:15:19 +02:00
|
|
|
return (
|
2026-04-07 21:46:38 +03:00
|
|
|
<div className="flex h-full min-h-0 flex-col overflow-hidden bg-sidebar">
|
|
|
|
|
<div className="h-9 bg-sidebar pt-1 px-2">
|
2026-03-02 02:11:33 +02:00
|
|
|
<SortableTabsStrip
|
|
|
|
|
items={tabItems}
|
|
|
|
|
activeId={rightSidebarTab}
|
|
|
|
|
onSelect={(tabID) => setRightSidebarTab(tabID as RightTab)}
|
|
|
|
|
layoutMode="fit"
|
|
|
|
|
variant="active-pill"
|
|
|
|
|
className="h-full"
|
2026-02-16 14:15:19 +02:00
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div className="min-h-0 flex-1 overflow-hidden">
|
2026-03-31 18:47:00 +03:00
|
|
|
{rightSidebarTab === 'git' && <GitView />}
|
|
|
|
|
{rightSidebarTab === 'files' && <SidebarFilesTree />}
|
2026-04-18 13:46:57 +03:00
|
|
|
{rightSidebarTab === 'context' && <ContextSidebarPanel />}
|
2026-02-16 14:15:19 +02:00
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
);
|
2026-04-18 13:46:57 +03:00
|
|
|
};
|