mirror of
https://github.com/siteboon/claudecodeui.git
synced 2026-05-08 13:38:24 +00:00
* refactor(ui): replace in-repo Command primitive with cmdk wrapper * feat(command-palette): add global Cmd+K palette with v1 actions * feat(command-palette): add session, file, and commit search sources * refactor: add provider names to model constants * feat(command-palette): add settings, navigation, message search, and ⌘K hints * feat(command-palette): add git fetch/pull/push and branch switch actions * refactor(command-palette): consolidate fetch source hooks behind useApiSource * refactor(command-palette): extract useCommandKey and SETTINGS_MAIN_TABS metadata * refactor(command-palette): extract groups into declarative registry * refactor(command-palette): wire openFile through PaletteOpsContext * refactor: migrate openSettings and refreshProjects from window.* to PaletteOpsContext * refactor(command-palette): inline groups and delete registry indirection * refactor(command-palette): return items array directly from source hooks * refactor(palette-ops): flatten Handle wrapper into ref-based registry * refactor: inline useCommandKey as MOD_KEY constant in two call sites * feat: introduce pages and fix bug on branch switching * fix: small labels * fix: coderabbit issues * fix: coderabbit comments * Update src/components/chat/view/subcomponents/ProviderSelectionEmptyState.tsx Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --------- Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
286 lines
9.3 KiB
TypeScript
286 lines
9.3 KiB
TypeScript
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 { usePaletteOps } from '../../../contexts/PaletteOpsContext';
|
|
import { useTasksSettings } from '../../../contexts/TasksSettingsContext';
|
|
import type { Project, LLMProvider } from '../../../types/app';
|
|
import type { MCPServerStatus, SidebarProps } from '../types/types';
|
|
|
|
import SidebarCollapsed from './subcomponents/SidebarCollapsed';
|
|
import SidebarContent from './subcomponents/SidebarContent';
|
|
import SidebarModals from './subcomponents/SidebarModals';
|
|
import type { SidebarProjectListProps } from './subcomponents/SidebarProjectList';
|
|
|
|
type TaskMasterSidebarContext = {
|
|
setCurrentProject: (project: Project) => void;
|
|
mcpServerStatus: MCPServerStatus;
|
|
};
|
|
|
|
function Sidebar({
|
|
projects,
|
|
selectedProject,
|
|
selectedSession,
|
|
onProjectSelect,
|
|
onSessionSelect,
|
|
onNewSession,
|
|
onSessionDelete,
|
|
onLoadMoreSessions,
|
|
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, installMode } = useVersionCheck(
|
|
'siteboon',
|
|
'claudecodeui',
|
|
);
|
|
const { preferences, setPreference } = useUiPreferences();
|
|
const { sidebarVisible } = preferences;
|
|
const { setCurrentProject, mcpServerStatus } = useTaskMaster() as TaskMasterSidebarContext;
|
|
const { tasksEnabled } = useTasksSettings();
|
|
const paletteOps = usePaletteOps();
|
|
|
|
const {
|
|
isSidebarCollapsed,
|
|
expandedProjects,
|
|
editingProject,
|
|
showNewProject,
|
|
editingName,
|
|
initialSessionsLoaded,
|
|
currentTime,
|
|
isRefreshing,
|
|
editingSession,
|
|
editingSessionName,
|
|
searchFilter,
|
|
searchMode,
|
|
setSearchMode,
|
|
conversationResults,
|
|
isSearching,
|
|
searchProgress,
|
|
clearConversationResults,
|
|
deletingProjects,
|
|
deleteConfirmation,
|
|
sessionDeleteConfirmation,
|
|
showVersionModal,
|
|
filteredProjects,
|
|
toggleProject,
|
|
handleSessionClick,
|
|
toggleStarProject,
|
|
isProjectStarred,
|
|
getProjectSessions,
|
|
loadingMoreProjects,
|
|
loadMoreSessionsForProject,
|
|
startEditing,
|
|
cancelEditing,
|
|
saveProjectName,
|
|
showDeleteSessionConfirmation,
|
|
confirmDeleteSession,
|
|
requestProjectDelete,
|
|
confirmDeleteProject,
|
|
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,
|
|
onLoadMoreSessions,
|
|
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 = () => {
|
|
void paletteOps.refreshProjects();
|
|
};
|
|
|
|
const projectListProps: SidebarProjectListProps = {
|
|
projects,
|
|
filteredProjects,
|
|
selectedProject,
|
|
selectedSession,
|
|
isLoading,
|
|
loadingProgress,
|
|
expandedProjects,
|
|
editingProject,
|
|
editingName,
|
|
initialSessionsLoaded,
|
|
currentTime,
|
|
editingSession,
|
|
editingSessionName,
|
|
deletingProjects,
|
|
tasksEnabled,
|
|
mcpServerStatus,
|
|
getProjectSessions,
|
|
loadingMoreProjects,
|
|
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: loadMoreSessionsForProject,
|
|
onNewSession,
|
|
onEditingSessionNameChange: setEditingSessionName,
|
|
onStartEditingSession: (sessionId, initialName) => {
|
|
setEditingSession(sessionId);
|
|
setEditingSessionName(initialName);
|
|
},
|
|
onCancelEditingSession: () => {
|
|
setEditingSession(null);
|
|
setEditingSessionName('');
|
|
},
|
|
onSaveEditingSession: (projectName: string, sessionId: string, summary: string, provider: LLMProvider) => {
|
|
void updateSessionSummary(projectName, sessionId, summary, provider);
|
|
},
|
|
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}
|
|
installMode={installMode}
|
|
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('')}
|
|
searchMode={searchMode}
|
|
onSearchModeChange={(mode: 'projects' | 'conversations') => {
|
|
setSearchMode(mode);
|
|
if (mode === 'projects') clearConversationResults();
|
|
}}
|
|
conversationResults={conversationResults}
|
|
isSearching={isSearching}
|
|
searchProgress={searchProgress}
|
|
onConversationResultClick={(projectId: string | null, sessionId: string, provider: string, messageTimestamp?: string | null, messageSnippet?: string | null) => {
|
|
// `projectId` (DB key) is the canonical identifier post-migration.
|
|
// The server emits null when it can't resolve a project row for
|
|
// the search hit; treat that as "no project" and still navigate
|
|
// to the session so the user can open it from the URL.
|
|
const resolvedProvider = (provider || 'claude') as LLMProvider;
|
|
const project = projectId ? projects.find(p => p.projectId === projectId) : null;
|
|
const searchTarget = { __searchTargetTimestamp: messageTimestamp || null, __searchTargetSnippet: messageSnippet || null };
|
|
const sessionObj = {
|
|
id: sessionId,
|
|
__provider: resolvedProvider,
|
|
__projectId: projectId ?? undefined,
|
|
...searchTarget,
|
|
};
|
|
if (project) {
|
|
handleProjectSelect(project);
|
|
const sessions = getProjectSessions(project);
|
|
const existing = sessions.find(s => s.id === sessionId);
|
|
if (existing) {
|
|
handleSessionClick({ ...existing, ...searchTarget }, project.projectId);
|
|
} else {
|
|
handleSessionClick(sessionObj, project.projectId);
|
|
}
|
|
} else {
|
|
handleSessionClick(sessionObj, projectId ?? '');
|
|
}
|
|
}}
|
|
onRefresh={() => {
|
|
void refreshProjects();
|
|
}}
|
|
isRefreshing={isRefreshing}
|
|
onCreateProject={() => setShowNewProject(true)}
|
|
onCollapseSidebar={handleCollapseSidebar}
|
|
updateAvailable={updateAvailable}
|
|
releaseInfo={releaseInfo}
|
|
latestVersion={latestVersion}
|
|
currentVersion={currentVersion}
|
|
onShowVersionModal={() => setShowVersionModal(true)}
|
|
onShowSettings={onShowSettings}
|
|
projectListProps={projectListProps}
|
|
t={t}
|
|
/>
|
|
</>
|
|
)}
|
|
|
|
</>
|
|
);
|
|
}
|
|
|
|
export default Sidebar;
|