Skip to content

Commit 122b757

Browse files
committed
feat: Add project sorting by date option
- Added ability to sort projects by name or most recent session activity - Added projectSortOrder state and localStorage persistence - Added UI controls in ToolsSettings to switch between sort modes - Projects with no sessions sort last when sorting by date - Real-time updates when sort preference changes
1 parent 4762a2d commit 122b757

2 files changed

Lines changed: 124 additions & 2 deletions

File tree

src/components/Sidebar.jsx

Lines changed: 68 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ function Sidebar({
6161
const [additionalSessions, setAdditionalSessions] = useState({});
6262
const [initialSessionsLoaded, setInitialSessionsLoaded] = useState(new Set());
6363
const [currentTime, setCurrentTime] = useState(new Date());
64+
const [projectSortOrder, setProjectSortOrder] = useState('name');
6465
const [isRefreshing, setIsRefreshing] = useState(false);
6566
const [editingSession, setEditingSession] = useState(null);
6667
const [editingSessionName, setEditingSessionName] = useState('');
@@ -114,6 +115,45 @@ function Sidebar({
114115
}
115116
}, [projects, isLoading]);
116117

118+
// Load project sort order from settings
119+
useEffect(() => {
120+
const loadSortOrder = () => {
121+
try {
122+
const savedSettings = localStorage.getItem('claude-tools-settings');
123+
if (savedSettings) {
124+
const settings = JSON.parse(savedSettings);
125+
setProjectSortOrder(settings.projectSortOrder || 'name');
126+
}
127+
} catch (error) {
128+
console.error('Error loading sort order:', error);
129+
}
130+
};
131+
132+
// Load initially
133+
loadSortOrder();
134+
135+
// Listen for storage changes
136+
const handleStorageChange = (e) => {
137+
if (e.key === 'claude-tools-settings') {
138+
loadSortOrder();
139+
}
140+
};
141+
142+
window.addEventListener('storage', handleStorageChange);
143+
144+
// Also check periodically when component is focused (for same-tab changes)
145+
const checkInterval = setInterval(() => {
146+
if (document.hasFocus()) {
147+
loadSortOrder();
148+
}
149+
}, 1000);
150+
151+
return () => {
152+
window.removeEventListener('storage', handleStorageChange);
153+
clearInterval(checkInterval);
154+
};
155+
}, []);
156+
117157
const toggleProject = (projectName) => {
118158
const newExpanded = new Set(expandedProjects);
119159
if (newExpanded.has(projectName)) {
@@ -291,6 +331,33 @@ function Sidebar({
291331
return [...initialSessions, ...additional];
292332
};
293333

334+
// Helper function to get the last activity date for a project
335+
const getProjectLastActivity = (project) => {
336+
const allSessions = getAllSessions(project);
337+
if (allSessions.length === 0) {
338+
return new Date(0); // Return epoch date for projects with no sessions
339+
}
340+
341+
// Find the most recent session activity
342+
const mostRecentDate = allSessions.reduce((latest, session) => {
343+
const sessionDate = new Date(session.lastActivity);
344+
return sessionDate > latest ? sessionDate : latest;
345+
}, new Date(0));
346+
347+
return mostRecentDate;
348+
};
349+
350+
// Sort projects based on selected order
351+
const sortedProjects = [...projects].sort((a, b) => {
352+
if (projectSortOrder === 'date') {
353+
// Sort by most recent activity (descending)
354+
return getProjectLastActivity(b) - getProjectLastActivity(a);
355+
} else {
356+
// Sort by name (ascending)
357+
return a.name.localeCompare(b.name);
358+
}
359+
});
360+
294361
return (
295362
<div className="h-full flex flex-col bg-card md:select-none">
296363
{/* Header */}
@@ -499,7 +566,7 @@ function Sidebar({
499566
</p>
500567
</div>
501568
) : (
502-
projects.map((project) => {
569+
sortedProjects.map((project) => {
503570
const isExpanded = expandedProjects.has(project.name);
504571
const isSelected = selectedProject?.name === project.name;
505572

src/components/ToolsSettings.jsx

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { Button } from './ui/button';
33
import { Input } from './ui/input';
44
import { ScrollArea } from './ui/scroll-area';
55
import { Badge } from './ui/badge';
6-
import { X, Plus, Settings, Shield, AlertTriangle, Moon, Sun } from 'lucide-react';
6+
import { X, Plus, Settings, Shield, AlertTriangle, Moon, Sun, FolderOpen, SortAsc } from 'lucide-react';
77
import { useTheme } from '../contexts/ThemeContext';
88

99
function ToolsSettings({ isOpen, onClose }) {
@@ -15,6 +15,7 @@ function ToolsSettings({ isOpen, onClose }) {
1515
const [skipPermissions, setSkipPermissions] = useState(false);
1616
const [isSaving, setIsSaving] = useState(false);
1717
const [saveStatus, setSaveStatus] = useState(null);
18+
const [projectSortOrder, setProjectSortOrder] = useState('name');
1819

1920
// Common tool patterns
2021
const commonTools = [
@@ -51,18 +52,21 @@ function ToolsSettings({ isOpen, onClose }) {
5152
setAllowedTools(settings.allowedTools || []);
5253
setDisallowedTools(settings.disallowedTools || []);
5354
setSkipPermissions(settings.skipPermissions || false);
55+
setProjectSortOrder(settings.projectSortOrder || 'name');
5456
} else {
5557
// Set defaults
5658
setAllowedTools([]);
5759
setDisallowedTools([]);
5860
setSkipPermissions(false);
61+
setProjectSortOrder('name');
5962
}
6063
} catch (error) {
6164
console.error('Error loading tool settings:', error);
6265
// Set defaults on error
6366
setAllowedTools([]);
6467
setDisallowedTools([]);
6568
setSkipPermissions(false);
69+
setProjectSortOrder('name');
6670
}
6771
};
6872

@@ -75,6 +79,7 @@ function ToolsSettings({ isOpen, onClose }) {
7579
allowedTools,
7680
disallowedTools,
7781
skipPermissions,
82+
projectSortOrder,
7883
lastUpdated: new Date().toISOString()
7984
};
8085

@@ -212,6 +217,56 @@ function ToolsSettings({ isOpen, onClose }) {
212217
</div>
213218
</div>
214219

220+
{/* Project Sorting */}
221+
<div className="space-y-4">
222+
<div className="flex items-center gap-3">
223+
<FolderOpen className="w-5 h-5 text-purple-500" />
224+
<h3 className="text-lg font-medium text-foreground">
225+
Project Sorting
226+
</h3>
227+
</div>
228+
<div className="bg-purple-50 dark:bg-purple-900/20 border border-purple-200 dark:border-purple-800 rounded-lg p-4">
229+
<div className="space-y-3">
230+
<div className="text-sm text-muted-foreground mb-3">
231+
Choose how projects are sorted in the sidebar
232+
</div>
233+
<div className="flex gap-2">
234+
<button
235+
onClick={() => setProjectSortOrder('name')}
236+
className={`flex-1 px-4 py-2 rounded-lg border transition-colors ${
237+
projectSortOrder === 'name'
238+
? 'bg-purple-600 text-white border-purple-600'
239+
: 'bg-white dark:bg-gray-800 text-gray-700 dark:text-gray-300 border-gray-300 dark:border-gray-600 hover:bg-gray-50 dark:hover:bg-gray-700'
240+
}`}
241+
>
242+
<div className="flex items-center justify-center gap-2">
243+
<SortAsc className="w-4 h-4" />
244+
<span>Sort by Name</span>
245+
</div>
246+
</button>
247+
<button
248+
onClick={() => setProjectSortOrder('date')}
249+
className={`flex-1 px-4 py-2 rounded-lg border transition-colors ${
250+
projectSortOrder === 'date'
251+
? 'bg-purple-600 text-white border-purple-600'
252+
: 'bg-white dark:bg-gray-800 text-gray-700 dark:text-gray-300 border-gray-300 dark:border-gray-600 hover:bg-gray-50 dark:hover:bg-gray-700'
253+
}`}
254+
>
255+
<div className="flex items-center justify-center gap-2">
256+
<SortAsc className="w-4 h-4 rotate-180" />
257+
<span>Sort by Date</span>
258+
</div>
259+
</button>
260+
</div>
261+
<div className="text-xs text-purple-700 dark:text-purple-300 mt-2">
262+
{projectSortOrder === 'name'
263+
? 'Projects are sorted alphabetically by name'
264+
: 'Projects are sorted by most recent session activity'}
265+
</div>
266+
</div>
267+
</div>
268+
</div>
269+
215270
{/* Allowed Tools */}
216271
<div className="space-y-4">
217272
<div className="flex items-center gap-3">

0 commit comments

Comments
 (0)