mirror of
https://github.com/siteboon/claudecodeui.git
synced 2026-02-15 21:27:35 +00:00
refactor: Restructure files and folders to better mimic feature-based architecture
This commit is contained in:
448
src/components/sidebar/hooks/useSidebarController.ts
Normal file
448
src/components/sidebar/hooks/useSidebarController.ts
Normal file
@@ -0,0 +1,448 @@
|
||||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import type React from 'react';
|
||||
import type { TFunction } from 'i18next';
|
||||
import { api } from '../../../utils/api';
|
||||
import type { Project, ProjectSession } from '../../../types/app';
|
||||
import type {
|
||||
AdditionalSessionsByProject,
|
||||
DeleteProjectConfirmation,
|
||||
LoadingSessionsByProject,
|
||||
ProjectSortOrder,
|
||||
SessionDeleteConfirmation,
|
||||
SessionWithProvider,
|
||||
} from '../types/types';
|
||||
import {
|
||||
filterProjects,
|
||||
getAllSessions,
|
||||
loadStarredProjects,
|
||||
persistStarredProjects,
|
||||
readProjectSortOrder,
|
||||
sortProjects,
|
||||
} from '../utils/utils';
|
||||
|
||||
type UseSidebarControllerArgs = {
|
||||
projects: Project[];
|
||||
selectedProject: Project | null;
|
||||
selectedSession: ProjectSession | null;
|
||||
isLoading: boolean;
|
||||
isMobile: boolean;
|
||||
t: TFunction;
|
||||
onRefresh: () => Promise<void> | void;
|
||||
onProjectSelect: (project: Project) => void;
|
||||
onSessionSelect: (session: ProjectSession) => void;
|
||||
onSessionDelete?: (sessionId: string) => void;
|
||||
onProjectDelete?: (projectName: string) => void;
|
||||
setCurrentProject: (project: Project) => void;
|
||||
setSidebarVisible: (visible: boolean) => void;
|
||||
sidebarVisible: boolean;
|
||||
};
|
||||
|
||||
export function useSidebarController({
|
||||
projects,
|
||||
selectedProject,
|
||||
selectedSession,
|
||||
isLoading,
|
||||
isMobile,
|
||||
t,
|
||||
onRefresh,
|
||||
onProjectSelect,
|
||||
onSessionSelect,
|
||||
onSessionDelete,
|
||||
onProjectDelete,
|
||||
setCurrentProject,
|
||||
setSidebarVisible,
|
||||
sidebarVisible,
|
||||
}: UseSidebarControllerArgs) {
|
||||
const [expandedProjects, setExpandedProjects] = useState<Set<string>>(new Set());
|
||||
const [editingProject, setEditingProject] = useState<string | null>(null);
|
||||
const [showNewProject, setShowNewProject] = useState(false);
|
||||
const [editingName, setEditingName] = useState('');
|
||||
const [loadingSessions, setLoadingSessions] = useState<LoadingSessionsByProject>({});
|
||||
const [additionalSessions, setAdditionalSessions] = useState<AdditionalSessionsByProject>({});
|
||||
const [initialSessionsLoaded, setInitialSessionsLoaded] = useState<Set<string>>(new Set());
|
||||
const [currentTime, setCurrentTime] = useState(new Date());
|
||||
const [projectSortOrder, setProjectSortOrder] = useState<ProjectSortOrder>('name');
|
||||
const [isRefreshing, setIsRefreshing] = useState(false);
|
||||
const [editingSession, setEditingSession] = useState<string | null>(null);
|
||||
const [editingSessionName, setEditingSessionName] = useState('');
|
||||
const [searchFilter, setSearchFilter] = useState('');
|
||||
const [deletingProjects, setDeletingProjects] = useState<Set<string>>(new Set());
|
||||
const [deleteConfirmation, setDeleteConfirmation] = useState<DeleteProjectConfirmation | null>(null);
|
||||
const [sessionDeleteConfirmation, setSessionDeleteConfirmation] = useState<SessionDeleteConfirmation | null>(null);
|
||||
const [showVersionModal, setShowVersionModal] = useState(false);
|
||||
const [starredProjects, setStarredProjects] = useState<Set<string>>(() => loadStarredProjects());
|
||||
|
||||
const isSidebarCollapsed = !isMobile && !sidebarVisible;
|
||||
|
||||
useEffect(() => {
|
||||
const timer = setInterval(() => {
|
||||
setCurrentTime(new Date());
|
||||
}, 60000);
|
||||
|
||||
return () => clearInterval(timer);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
setAdditionalSessions({});
|
||||
setInitialSessionsLoaded(new Set());
|
||||
}, [projects]);
|
||||
|
||||
useEffect(() => {
|
||||
if (selectedSession && selectedProject) {
|
||||
setExpandedProjects((prev) => {
|
||||
if (prev.has(selectedProject.name)) {
|
||||
return prev;
|
||||
}
|
||||
const next = new Set(prev);
|
||||
next.add(selectedProject.name);
|
||||
return next;
|
||||
});
|
||||
}
|
||||
}, [selectedSession, selectedProject]);
|
||||
|
||||
useEffect(() => {
|
||||
if (projects.length > 0 && !isLoading) {
|
||||
const loadedProjects = new Set<string>();
|
||||
projects.forEach((project) => {
|
||||
if (project.sessions && project.sessions.length >= 0) {
|
||||
loadedProjects.add(project.name);
|
||||
}
|
||||
});
|
||||
setInitialSessionsLoaded(loadedProjects);
|
||||
}
|
||||
}, [projects, isLoading]);
|
||||
|
||||
useEffect(() => {
|
||||
const loadSortOrder = () => {
|
||||
setProjectSortOrder(readProjectSortOrder());
|
||||
};
|
||||
|
||||
loadSortOrder();
|
||||
|
||||
const handleStorageChange = (event: StorageEvent) => {
|
||||
if (event.key === 'claude-settings') {
|
||||
loadSortOrder();
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener('storage', handleStorageChange);
|
||||
|
||||
const interval = setInterval(() => {
|
||||
if (document.hasFocus()) {
|
||||
loadSortOrder();
|
||||
}
|
||||
}, 1000);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('storage', handleStorageChange);
|
||||
clearInterval(interval);
|
||||
};
|
||||
}, []);
|
||||
|
||||
const handleTouchClick = useCallback(
|
||||
(callback: () => void) =>
|
||||
(event: React.TouchEvent<HTMLElement>) => {
|
||||
const target = event.target as HTMLElement;
|
||||
if (target.closest('.overflow-y-auto') || target.closest('[data-scroll-container]')) {
|
||||
return;
|
||||
}
|
||||
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
callback();
|
||||
},
|
||||
[],
|
||||
);
|
||||
|
||||
const toggleProject = useCallback((projectName: string) => {
|
||||
setExpandedProjects((prev) => {
|
||||
const next = new Set<string>();
|
||||
if (!prev.has(projectName)) {
|
||||
next.add(projectName);
|
||||
}
|
||||
return next;
|
||||
});
|
||||
}, []);
|
||||
|
||||
const handleSessionClick = useCallback(
|
||||
(session: SessionWithProvider, projectName: string) => {
|
||||
onSessionSelect({ ...session, __projectName: projectName });
|
||||
},
|
||||
[onSessionSelect],
|
||||
);
|
||||
|
||||
const toggleStarProject = useCallback((projectName: string) => {
|
||||
setStarredProjects((prev) => {
|
||||
const next = new Set(prev);
|
||||
if (next.has(projectName)) {
|
||||
next.delete(projectName);
|
||||
} else {
|
||||
next.add(projectName);
|
||||
}
|
||||
|
||||
persistStarredProjects(next);
|
||||
return next;
|
||||
});
|
||||
}, []);
|
||||
|
||||
const isProjectStarred = useCallback(
|
||||
(projectName: string) => starredProjects.has(projectName),
|
||||
[starredProjects],
|
||||
);
|
||||
|
||||
const getProjectSessions = useCallback(
|
||||
(project: Project) => getAllSessions(project, additionalSessions),
|
||||
[additionalSessions],
|
||||
);
|
||||
|
||||
const sortedProjects = useMemo(
|
||||
() => sortProjects(projects, projectSortOrder, starredProjects, additionalSessions),
|
||||
[additionalSessions, projectSortOrder, projects, starredProjects],
|
||||
);
|
||||
|
||||
const filteredProjects = useMemo(
|
||||
() => filterProjects(sortedProjects, searchFilter),
|
||||
[searchFilter, sortedProjects],
|
||||
);
|
||||
|
||||
const startEditing = useCallback((project: Project) => {
|
||||
setEditingProject(project.name);
|
||||
setEditingName(project.displayName);
|
||||
}, []);
|
||||
|
||||
const cancelEditing = useCallback(() => {
|
||||
setEditingProject(null);
|
||||
setEditingName('');
|
||||
}, []);
|
||||
|
||||
const saveProjectName = useCallback(
|
||||
async (projectName: string) => {
|
||||
try {
|
||||
const response = await api.renameProject(projectName, editingName);
|
||||
if (response.ok) {
|
||||
if (window.refreshProjects) {
|
||||
await window.refreshProjects();
|
||||
} else {
|
||||
window.location.reload();
|
||||
}
|
||||
} else {
|
||||
console.error('Failed to rename project');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error renaming project:', error);
|
||||
} finally {
|
||||
setEditingProject(null);
|
||||
setEditingName('');
|
||||
}
|
||||
},
|
||||
[editingName],
|
||||
);
|
||||
|
||||
const showDeleteSessionConfirmation = useCallback(
|
||||
(
|
||||
projectName: string,
|
||||
sessionId: string,
|
||||
sessionTitle: string,
|
||||
provider: SessionDeleteConfirmation['provider'] = 'claude',
|
||||
) => {
|
||||
setSessionDeleteConfirmation({ projectName, sessionId, sessionTitle, provider });
|
||||
},
|
||||
[],
|
||||
);
|
||||
|
||||
const confirmDeleteSession = useCallback(async () => {
|
||||
if (!sessionDeleteConfirmation) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { projectName, sessionId, provider } = sessionDeleteConfirmation;
|
||||
setSessionDeleteConfirmation(null);
|
||||
|
||||
try {
|
||||
const response =
|
||||
provider === 'codex'
|
||||
? await api.deleteCodexSession(sessionId)
|
||||
: await api.deleteSession(projectName, sessionId);
|
||||
|
||||
if (response.ok) {
|
||||
onSessionDelete?.(sessionId);
|
||||
} else {
|
||||
const errorText = await response.text();
|
||||
console.error('[Sidebar] Failed to delete session:', {
|
||||
status: response.status,
|
||||
error: errorText,
|
||||
});
|
||||
alert(t('messages.deleteSessionFailed'));
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('[Sidebar] Error deleting session:', error);
|
||||
alert(t('messages.deleteSessionError'));
|
||||
}
|
||||
}, [onSessionDelete, sessionDeleteConfirmation, t]);
|
||||
|
||||
const requestProjectDelete = useCallback(
|
||||
(project: Project) => {
|
||||
setDeleteConfirmation({
|
||||
project,
|
||||
sessionCount: getProjectSessions(project).length,
|
||||
});
|
||||
},
|
||||
[getProjectSessions],
|
||||
);
|
||||
|
||||
const confirmDeleteProject = useCallback(async () => {
|
||||
if (!deleteConfirmation) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { project, sessionCount } = deleteConfirmation;
|
||||
const isEmpty = sessionCount === 0;
|
||||
|
||||
setDeleteConfirmation(null);
|
||||
setDeletingProjects((prev) => new Set([...prev, project.name]));
|
||||
|
||||
try {
|
||||
const response = await api.deleteProject(project.name, !isEmpty);
|
||||
|
||||
if (response.ok) {
|
||||
onProjectDelete?.(project.name);
|
||||
} else {
|
||||
const error = (await response.json()) as { error?: string };
|
||||
alert(error.error || t('messages.deleteProjectFailed'));
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error deleting project:', error);
|
||||
alert(t('messages.deleteProjectError'));
|
||||
} finally {
|
||||
setDeletingProjects((prev) => {
|
||||
const next = new Set(prev);
|
||||
next.delete(project.name);
|
||||
return next;
|
||||
});
|
||||
}
|
||||
}, [deleteConfirmation, onProjectDelete, t]);
|
||||
|
||||
const loadMoreSessions = useCallback(
|
||||
async (project: Project) => {
|
||||
const canLoadMore = project.sessionMeta?.hasMore === true;
|
||||
if (!canLoadMore || loadingSessions[project.name]) {
|
||||
return;
|
||||
}
|
||||
|
||||
setLoadingSessions((prev) => ({ ...prev, [project.name]: true }));
|
||||
|
||||
try {
|
||||
const currentSessionCount =
|
||||
(project.sessions?.length || 0) + (additionalSessions[project.name]?.length || 0);
|
||||
const response = await api.sessions(project.name, 5, currentSessionCount);
|
||||
|
||||
if (!response.ok) {
|
||||
return;
|
||||
}
|
||||
|
||||
const result = (await response.json()) as {
|
||||
sessions?: ProjectSession[];
|
||||
hasMore?: boolean;
|
||||
};
|
||||
|
||||
setAdditionalSessions((prev) => ({
|
||||
...prev,
|
||||
[project.name]: [...(prev[project.name] || []), ...(result.sessions || [])],
|
||||
}));
|
||||
|
||||
if (result.hasMore === false) {
|
||||
project.sessionMeta = { ...project.sessionMeta, hasMore: false };
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error loading more sessions:', error);
|
||||
} finally {
|
||||
setLoadingSessions((prev) => ({ ...prev, [project.name]: false }));
|
||||
}
|
||||
},
|
||||
[additionalSessions, loadingSessions],
|
||||
);
|
||||
|
||||
const handleProjectSelect = useCallback(
|
||||
(project: Project) => {
|
||||
onProjectSelect(project);
|
||||
setCurrentProject(project);
|
||||
},
|
||||
[onProjectSelect, setCurrentProject],
|
||||
);
|
||||
|
||||
const refreshProjects = useCallback(async () => {
|
||||
setIsRefreshing(true);
|
||||
try {
|
||||
await onRefresh();
|
||||
} finally {
|
||||
setIsRefreshing(false);
|
||||
}
|
||||
}, [onRefresh]);
|
||||
|
||||
const updateSessionSummary = useCallback(
|
||||
async (_projectName: string, _sessionId: string, _summary: string) => {
|
||||
// Session rename endpoint is not currently exposed on the API.
|
||||
setEditingSession(null);
|
||||
setEditingSessionName('');
|
||||
},
|
||||
[],
|
||||
);
|
||||
|
||||
const collapseSidebar = useCallback(() => {
|
||||
setSidebarVisible(false);
|
||||
}, [setSidebarVisible]);
|
||||
|
||||
const expandSidebar = useCallback(() => {
|
||||
setSidebarVisible(true);
|
||||
}, [setSidebarVisible]);
|
||||
|
||||
return {
|
||||
isSidebarCollapsed,
|
||||
expandedProjects,
|
||||
editingProject,
|
||||
showNewProject,
|
||||
editingName,
|
||||
loadingSessions,
|
||||
additionalSessions,
|
||||
initialSessionsLoaded,
|
||||
currentTime,
|
||||
projectSortOrder,
|
||||
isRefreshing,
|
||||
editingSession,
|
||||
editingSessionName,
|
||||
searchFilter,
|
||||
deletingProjects,
|
||||
deleteConfirmation,
|
||||
sessionDeleteConfirmation,
|
||||
showVersionModal,
|
||||
starredProjects,
|
||||
filteredProjects,
|
||||
handleTouchClick,
|
||||
toggleProject,
|
||||
handleSessionClick,
|
||||
toggleStarProject,
|
||||
isProjectStarred,
|
||||
getProjectSessions,
|
||||
startEditing,
|
||||
cancelEditing,
|
||||
saveProjectName,
|
||||
showDeleteSessionConfirmation,
|
||||
confirmDeleteSession,
|
||||
requestProjectDelete,
|
||||
confirmDeleteProject,
|
||||
loadMoreSessions,
|
||||
handleProjectSelect,
|
||||
refreshProjects,
|
||||
updateSessionSummary,
|
||||
collapseSidebar,
|
||||
expandSidebar,
|
||||
setShowNewProject,
|
||||
setEditingName,
|
||||
setEditingSession,
|
||||
setEditingSessionName,
|
||||
setSearchFilter,
|
||||
setDeleteConfirmation,
|
||||
setSessionDeleteConfirmation,
|
||||
setShowVersionModal,
|
||||
};
|
||||
}
|
||||
@@ -1,11 +1,11 @@
|
||||
import type { TFunction } from 'i18next';
|
||||
import type { Project } from '../../types/app';
|
||||
import type { Project } from '../../../types/app';
|
||||
import type {
|
||||
AdditionalSessionsByProject,
|
||||
ProjectSortOrder,
|
||||
SessionViewModel,
|
||||
SessionWithProvider,
|
||||
} from './types';
|
||||
} from '../types/types';
|
||||
|
||||
export const readProjectSortOrder = (): ProjectSortOrder => {
|
||||
try {
|
||||
245
src/components/sidebar/view/Sidebar.tsx
Normal file
245
src/components/sidebar/view/Sidebar.tsx
Normal file
@@ -0,0 +1,245 @@
|
||||
import { useEffect } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useDeviceSettings } from '../../../hooks/useDeviceSettings';
|
||||
import { useVersionCheck } from '../../../hooks/useVersionCheck';
|
||||
import { useUiPreferences } from '../../../hooks/useUiPreferences';
|
||||
import { useSidebarController } from '../hooks/useSidebarController';
|
||||
import { useTaskMaster } from '../../../contexts/TaskMasterContext';
|
||||
import { useTasksSettings } from '../../../contexts/TasksSettingsContext';
|
||||
import SidebarCollapsed from './subcomponents/SidebarCollapsed';
|
||||
import SidebarContent from './subcomponents/SidebarContent';
|
||||
import SidebarModals from './subcomponents/SidebarModals';
|
||||
import type { Project } from '../../../types/app';
|
||||
import type { SidebarProjectListProps } from './subcomponents/SidebarProjectList';
|
||||
import type { MCPServerStatus, SidebarProps } from '../types/types';
|
||||
|
||||
type TaskMasterSidebarContext = {
|
||||
setCurrentProject: (project: Project) => void;
|
||||
mcpServerStatus: MCPServerStatus;
|
||||
};
|
||||
|
||||
function Sidebar({
|
||||
projects,
|
||||
selectedProject,
|
||||
selectedSession,
|
||||
onProjectSelect,
|
||||
onSessionSelect,
|
||||
onNewSession,
|
||||
onSessionDelete,
|
||||
onProjectDelete,
|
||||
isLoading,
|
||||
loadingProgress,
|
||||
onRefresh,
|
||||
onShowSettings,
|
||||
showSettings,
|
||||
settingsInitialTab,
|
||||
onCloseSettings,
|
||||
isMobile,
|
||||
}: SidebarProps) {
|
||||
const { t } = useTranslation(['sidebar', 'common']);
|
||||
const { isPWA } = useDeviceSettings({ trackMobile: false });
|
||||
const { updateAvailable, latestVersion, currentVersion, releaseInfo } = useVersionCheck(
|
||||
'siteboon',
|
||||
'claudecodeui',
|
||||
);
|
||||
const { preferences, setPreference } = useUiPreferences();
|
||||
const { sidebarVisible } = preferences;
|
||||
const { setCurrentProject, mcpServerStatus } = useTaskMaster() as TaskMasterSidebarContext;
|
||||
const { tasksEnabled } = useTasksSettings();
|
||||
|
||||
const {
|
||||
isSidebarCollapsed,
|
||||
expandedProjects,
|
||||
editingProject,
|
||||
showNewProject,
|
||||
editingName,
|
||||
loadingSessions,
|
||||
initialSessionsLoaded,
|
||||
currentTime,
|
||||
isRefreshing,
|
||||
editingSession,
|
||||
editingSessionName,
|
||||
searchFilter,
|
||||
deletingProjects,
|
||||
deleteConfirmation,
|
||||
sessionDeleteConfirmation,
|
||||
showVersionModal,
|
||||
filteredProjects,
|
||||
handleTouchClick,
|
||||
toggleProject,
|
||||
handleSessionClick,
|
||||
toggleStarProject,
|
||||
isProjectStarred,
|
||||
getProjectSessions,
|
||||
startEditing,
|
||||
cancelEditing,
|
||||
saveProjectName,
|
||||
showDeleteSessionConfirmation,
|
||||
confirmDeleteSession,
|
||||
requestProjectDelete,
|
||||
confirmDeleteProject,
|
||||
loadMoreSessions,
|
||||
handleProjectSelect,
|
||||
refreshProjects,
|
||||
updateSessionSummary,
|
||||
collapseSidebar: handleCollapseSidebar,
|
||||
expandSidebar: handleExpandSidebar,
|
||||
setShowNewProject,
|
||||
setEditingName,
|
||||
setEditingSession,
|
||||
setEditingSessionName,
|
||||
setSearchFilter,
|
||||
setDeleteConfirmation,
|
||||
setSessionDeleteConfirmation,
|
||||
setShowVersionModal,
|
||||
} = useSidebarController({
|
||||
projects,
|
||||
selectedProject,
|
||||
selectedSession,
|
||||
isLoading,
|
||||
isMobile,
|
||||
t,
|
||||
onRefresh,
|
||||
onProjectSelect,
|
||||
onSessionSelect,
|
||||
onSessionDelete,
|
||||
onProjectDelete,
|
||||
setCurrentProject,
|
||||
setSidebarVisible: (visible) => setPreference('sidebarVisible', visible),
|
||||
sidebarVisible,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (typeof document === 'undefined') {
|
||||
return;
|
||||
}
|
||||
|
||||
document.documentElement.classList.toggle('pwa-mode', isPWA);
|
||||
document.body.classList.toggle('pwa-mode', isPWA);
|
||||
}, [isPWA]);
|
||||
|
||||
const handleProjectCreated = () => {
|
||||
if (window.refreshProjects) {
|
||||
void window.refreshProjects();
|
||||
return;
|
||||
}
|
||||
|
||||
window.location.reload();
|
||||
};
|
||||
|
||||
const projectListProps: SidebarProjectListProps = {
|
||||
projects,
|
||||
filteredProjects,
|
||||
selectedProject,
|
||||
selectedSession,
|
||||
isLoading,
|
||||
loadingProgress,
|
||||
expandedProjects,
|
||||
editingProject,
|
||||
editingName,
|
||||
loadingSessions,
|
||||
initialSessionsLoaded,
|
||||
currentTime,
|
||||
editingSession,
|
||||
editingSessionName,
|
||||
deletingProjects,
|
||||
tasksEnabled,
|
||||
mcpServerStatus,
|
||||
getProjectSessions,
|
||||
isProjectStarred,
|
||||
onEditingNameChange: setEditingName,
|
||||
onToggleProject: toggleProject,
|
||||
onProjectSelect: handleProjectSelect,
|
||||
onToggleStarProject: toggleStarProject,
|
||||
onStartEditingProject: startEditing,
|
||||
onCancelEditingProject: cancelEditing,
|
||||
onSaveProjectName: (projectName) => {
|
||||
void saveProjectName(projectName);
|
||||
},
|
||||
onDeleteProject: requestProjectDelete,
|
||||
onSessionSelect: handleSessionClick,
|
||||
onDeleteSession: showDeleteSessionConfirmation,
|
||||
onLoadMoreSessions: (project) => {
|
||||
void loadMoreSessions(project);
|
||||
},
|
||||
onNewSession,
|
||||
onEditingSessionNameChange: setEditingSessionName,
|
||||
onStartEditingSession: (sessionId, initialName) => {
|
||||
setEditingSession(sessionId);
|
||||
setEditingSessionName(initialName);
|
||||
},
|
||||
onCancelEditingSession: () => {
|
||||
setEditingSession(null);
|
||||
setEditingSessionName('');
|
||||
},
|
||||
onSaveEditingSession: (projectName, sessionId, summary) => {
|
||||
void updateSessionSummary(projectName, sessionId, summary);
|
||||
},
|
||||
touchHandlerFactory: handleTouchClick,
|
||||
t,
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<SidebarModals
|
||||
projects={projects}
|
||||
showSettings={showSettings}
|
||||
settingsInitialTab={settingsInitialTab}
|
||||
onCloseSettings={onCloseSettings}
|
||||
showNewProject={showNewProject}
|
||||
onCloseNewProject={() => setShowNewProject(false)}
|
||||
onProjectCreated={handleProjectCreated}
|
||||
deleteConfirmation={deleteConfirmation}
|
||||
onCancelDeleteProject={() => setDeleteConfirmation(null)}
|
||||
onConfirmDeleteProject={confirmDeleteProject}
|
||||
sessionDeleteConfirmation={sessionDeleteConfirmation}
|
||||
onCancelDeleteSession={() => setSessionDeleteConfirmation(null)}
|
||||
onConfirmDeleteSession={confirmDeleteSession}
|
||||
showVersionModal={showVersionModal}
|
||||
onCloseVersionModal={() => setShowVersionModal(false)}
|
||||
releaseInfo={releaseInfo}
|
||||
currentVersion={currentVersion}
|
||||
latestVersion={latestVersion}
|
||||
t={t}
|
||||
/>
|
||||
|
||||
{isSidebarCollapsed ? (
|
||||
<SidebarCollapsed
|
||||
onExpand={handleExpandSidebar}
|
||||
onShowSettings={onShowSettings}
|
||||
updateAvailable={updateAvailable}
|
||||
onShowVersionModal={() => setShowVersionModal(true)}
|
||||
t={t}
|
||||
/>
|
||||
) : (
|
||||
<>
|
||||
<SidebarContent
|
||||
isPWA={isPWA}
|
||||
isMobile={isMobile}
|
||||
isLoading={isLoading}
|
||||
projects={projects}
|
||||
searchFilter={searchFilter}
|
||||
onSearchFilterChange={setSearchFilter}
|
||||
onClearSearchFilter={() => setSearchFilter('')}
|
||||
onRefresh={() => {
|
||||
void refreshProjects();
|
||||
}}
|
||||
isRefreshing={isRefreshing}
|
||||
onCreateProject={() => setShowNewProject(true)}
|
||||
onCollapseSidebar={handleCollapseSidebar}
|
||||
updateAvailable={updateAvailable}
|
||||
releaseInfo={releaseInfo}
|
||||
latestVersion={latestVersion}
|
||||
onShowVersionModal={() => setShowVersionModal(true)}
|
||||
onShowSettings={onShowSettings}
|
||||
projectListProps={projectListProps}
|
||||
t={t}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default Sidebar;
|
||||
@@ -1,7 +1,7 @@
|
||||
import { ScrollArea } from '../ui/scroll-area';
|
||||
import { ScrollArea } from '../../../ui/scroll-area';
|
||||
import type { TFunction } from 'i18next';
|
||||
import type { Project } from '../../types/app';
|
||||
import type { ReleaseInfo } from '../../types/sharedTypes';
|
||||
import type { Project } from '../../../../types/app';
|
||||
import type { ReleaseInfo } from '../../../../types/sharedTypes';
|
||||
import SidebarFooter from './SidebarFooter';
|
||||
import SidebarHeader from './SidebarHeader';
|
||||
import SidebarProjectList, { type SidebarProjectListProps } from './SidebarProjectList';
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Settings } from 'lucide-react';
|
||||
import type { TFunction } from 'i18next';
|
||||
import type { ReleaseInfo } from '../../types/sharedTypes';
|
||||
import { Button } from '../ui/button';
|
||||
import type { ReleaseInfo } from '../../../../types/sharedTypes';
|
||||
import { Button } from '../../../ui/button';
|
||||
|
||||
type SidebarFooterProps = {
|
||||
updateAvailable: boolean;
|
||||
@@ -1,8 +1,8 @@
|
||||
import { FolderPlus, MessageSquare, RefreshCw, Search, X } from 'lucide-react';
|
||||
import type { TFunction } from 'i18next';
|
||||
import { Button } from '../ui/button';
|
||||
import { Input } from '../ui/input';
|
||||
import { IS_PLATFORM } from '../../constants/config';
|
||||
import { Button } from '../../../ui/button';
|
||||
import { Input } from '../../../ui/input';
|
||||
import { IS_PLATFORM } from '../../../../constants/config';
|
||||
|
||||
type SidebarHeaderProps = {
|
||||
isPWA: boolean;
|
||||
@@ -1,13 +1,13 @@
|
||||
import ReactDOM from 'react-dom';
|
||||
import { AlertTriangle, Trash2 } from 'lucide-react';
|
||||
import type { TFunction } from 'i18next';
|
||||
import { Button } from '../ui/button';
|
||||
import ProjectCreationWizard from '../ProjectCreationWizard';
|
||||
import Settings from '../Settings';
|
||||
import VersionUpgradeModal from '../modals/VersionUpgradeModal';
|
||||
import type { Project } from '../../types/app';
|
||||
import type { ReleaseInfo } from '../../types/sharedTypes';
|
||||
import type { DeleteProjectConfirmation, SessionDeleteConfirmation } from './types';
|
||||
import { Button } from '../../../ui/button';
|
||||
import ProjectCreationWizard from '../../../ProjectCreationWizard';
|
||||
import Settings from '../../../Settings';
|
||||
import VersionUpgradeModal from '../../../modals/VersionUpgradeModal';
|
||||
import type { Project } from '../../../../types/app';
|
||||
import type { ReleaseInfo } from '../../../../types/sharedTypes';
|
||||
import type { DeleteProjectConfirmation, SessionDeleteConfirmation } from '../../types/types';
|
||||
|
||||
type SidebarModalsProps = {
|
||||
projects: Project[];
|
||||
@@ -1,11 +1,11 @@
|
||||
import { Button } from '../ui/button';
|
||||
import { Button } from '../../../ui/button';
|
||||
import { Check, ChevronDown, ChevronRight, Edit3, Folder, FolderOpen, Star, Trash2, X } from 'lucide-react';
|
||||
import type { TFunction } from 'i18next';
|
||||
import { cn } from '../../lib/utils';
|
||||
import TaskIndicator from '../TaskIndicator';
|
||||
import type { Project, ProjectSession, SessionProvider } from '../../types/app';
|
||||
import type { MCPServerStatus, SessionWithProvider, TouchHandlerFactory } from './types';
|
||||
import { getTaskIndicatorStatus } from './utils';
|
||||
import { cn } from '../../../../lib/utils';
|
||||
import TaskIndicator from '../../../TaskIndicator';
|
||||
import type { Project, ProjectSession, SessionProvider } from '../../../../types/app';
|
||||
import type { MCPServerStatus, SessionWithProvider, TouchHandlerFactory } from '../../types/types';
|
||||
import { getTaskIndicatorStatus } from '../../utils/utils';
|
||||
import SidebarProjectSessions from './SidebarProjectSessions';
|
||||
|
||||
type SidebarProjectItemProps = {
|
||||
@@ -1,11 +1,11 @@
|
||||
import type { TFunction } from 'i18next';
|
||||
import type { LoadingProgress, Project, ProjectSession, SessionProvider } from '../../types/app';
|
||||
import type { LoadingProgress, Project, ProjectSession, SessionProvider } from '../../../../types/app';
|
||||
import type {
|
||||
LoadingSessionsByProject,
|
||||
MCPServerStatus,
|
||||
SessionWithProvider,
|
||||
TouchHandlerFactory,
|
||||
} from './types';
|
||||
} from '../../types/types';
|
||||
import SidebarProjectItem from './SidebarProjectItem';
|
||||
import SidebarProjectsState from './SidebarProjectsState';
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { ChevronDown, Plus } from 'lucide-react';
|
||||
import type { TFunction } from 'i18next';
|
||||
import { Button } from '../ui/button';
|
||||
import type { Project, ProjectSession, SessionProvider } from '../../types/app';
|
||||
import type { SessionWithProvider, TouchHandlerFactory } from './types';
|
||||
import { Button } from '../../../ui/button';
|
||||
import type { Project, ProjectSession, SessionProvider } from '../../../../types/app';
|
||||
import type { SessionWithProvider, TouchHandlerFactory } from '../../types/types';
|
||||
import SidebarSessionItem from './SidebarSessionItem';
|
||||
|
||||
type SidebarProjectSessionsProps = {
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Folder, Search } from 'lucide-react';
|
||||
import type { TFunction } from 'i18next';
|
||||
import type { LoadingProgress } from '../../types/app';
|
||||
import type { LoadingProgress } from '../../../../types/app';
|
||||
|
||||
type SidebarProjectsStateProps = {
|
||||
isLoading: boolean;
|
||||
@@ -1,13 +1,13 @@
|
||||
import { Badge } from '../ui/badge';
|
||||
import { Button } from '../ui/button';
|
||||
import { Badge } from '../../../ui/badge';
|
||||
import { Button } from '../../../ui/button';
|
||||
import { Check, Clock, Edit2, Trash2, X } from 'lucide-react';
|
||||
import type { TFunction } from 'i18next';
|
||||
import { cn } from '../../lib/utils';
|
||||
import { formatTimeAgo } from '../../utils/dateUtils';
|
||||
import type { Project, ProjectSession, SessionProvider } from '../../types/app';
|
||||
import type { SessionWithProvider, TouchHandlerFactory } from './types';
|
||||
import { createSessionViewModel } from './utils';
|
||||
import SessionProviderLogo from '../SessionProviderLogo';
|
||||
import { cn } from '../../../../lib/utils';
|
||||
import { formatTimeAgo } from '../../../../utils/dateUtils';
|
||||
import type { Project, ProjectSession, SessionProvider } from '../../../../types/app';
|
||||
import type { SessionWithProvider, TouchHandlerFactory } from '../../types/types';
|
||||
import { createSessionViewModel } from '../../utils/utils';
|
||||
import SessionProviderLogo from '../../../SessionProviderLogo';
|
||||
|
||||
type SidebarSessionItemProps = {
|
||||
project: Project;
|
||||
Reference in New Issue
Block a user