From 122b757fa2db5cb8d878109c65e549a376733f12 Mon Sep 17 00:00:00 2001 From: Valics Lehel Date: Fri, 11 Jul 2025 22:42:22 +0300 Subject: [PATCH 1/5] 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 --- src/components/Sidebar.jsx | 69 +++++++++++++++++++++++++++++++- src/components/ToolsSettings.jsx | 57 +++++++++++++++++++++++++- 2 files changed, 124 insertions(+), 2 deletions(-) diff --git a/src/components/Sidebar.jsx b/src/components/Sidebar.jsx index 8c19771..598b093 100755 --- a/src/components/Sidebar.jsx +++ b/src/components/Sidebar.jsx @@ -61,6 +61,7 @@ function Sidebar({ const [additionalSessions, setAdditionalSessions] = useState({}); const [initialSessionsLoaded, setInitialSessionsLoaded] = useState(new Set()); const [currentTime, setCurrentTime] = useState(new Date()); + const [projectSortOrder, setProjectSortOrder] = useState('name'); const [isRefreshing, setIsRefreshing] = useState(false); const [editingSession, setEditingSession] = useState(null); const [editingSessionName, setEditingSessionName] = useState(''); @@ -114,6 +115,45 @@ function Sidebar({ } }, [projects, isLoading]); + // Load project sort order from settings + useEffect(() => { + const loadSortOrder = () => { + try { + const savedSettings = localStorage.getItem('claude-tools-settings'); + if (savedSettings) { + const settings = JSON.parse(savedSettings); + setProjectSortOrder(settings.projectSortOrder || 'name'); + } + } catch (error) { + console.error('Error loading sort order:', error); + } + }; + + // Load initially + loadSortOrder(); + + // Listen for storage changes + const handleStorageChange = (e) => { + if (e.key === 'claude-tools-settings') { + loadSortOrder(); + } + }; + + window.addEventListener('storage', handleStorageChange); + + // Also check periodically when component is focused (for same-tab changes) + const checkInterval = setInterval(() => { + if (document.hasFocus()) { + loadSortOrder(); + } + }, 1000); + + return () => { + window.removeEventListener('storage', handleStorageChange); + clearInterval(checkInterval); + }; + }, []); + const toggleProject = (projectName) => { const newExpanded = new Set(expandedProjects); if (newExpanded.has(projectName)) { @@ -291,6 +331,33 @@ function Sidebar({ return [...initialSessions, ...additional]; }; + // Helper function to get the last activity date for a project + const getProjectLastActivity = (project) => { + const allSessions = getAllSessions(project); + if (allSessions.length === 0) { + return new Date(0); // Return epoch date for projects with no sessions + } + + // Find the most recent session activity + const mostRecentDate = allSessions.reduce((latest, session) => { + const sessionDate = new Date(session.lastActivity); + return sessionDate > latest ? sessionDate : latest; + }, new Date(0)); + + return mostRecentDate; + }; + + // Sort projects based on selected order + const sortedProjects = [...projects].sort((a, b) => { + if (projectSortOrder === 'date') { + // Sort by most recent activity (descending) + return getProjectLastActivity(b) - getProjectLastActivity(a); + } else { + // Sort by name (ascending) + return a.name.localeCompare(b.name); + } + }); + return (
{/* Header */} @@ -499,7 +566,7 @@ function Sidebar({

) : ( - projects.map((project) => { + sortedProjects.map((project) => { const isExpanded = expandedProjects.has(project.name); const isSelected = selectedProject?.name === project.name; diff --git a/src/components/ToolsSettings.jsx b/src/components/ToolsSettings.jsx index da4aae2..7e5295a 100755 --- a/src/components/ToolsSettings.jsx +++ b/src/components/ToolsSettings.jsx @@ -3,7 +3,7 @@ import { Button } from './ui/button'; import { Input } from './ui/input'; import { ScrollArea } from './ui/scroll-area'; import { Badge } from './ui/badge'; -import { X, Plus, Settings, Shield, AlertTriangle, Moon, Sun } from 'lucide-react'; +import { X, Plus, Settings, Shield, AlertTriangle, Moon, Sun, FolderOpen, SortAsc } from 'lucide-react'; import { useTheme } from '../contexts/ThemeContext'; function ToolsSettings({ isOpen, onClose }) { @@ -15,6 +15,7 @@ function ToolsSettings({ isOpen, onClose }) { const [skipPermissions, setSkipPermissions] = useState(false); const [isSaving, setIsSaving] = useState(false); const [saveStatus, setSaveStatus] = useState(null); + const [projectSortOrder, setProjectSortOrder] = useState('name'); // Common tool patterns const commonTools = [ @@ -51,11 +52,13 @@ function ToolsSettings({ isOpen, onClose }) { setAllowedTools(settings.allowedTools || []); setDisallowedTools(settings.disallowedTools || []); setSkipPermissions(settings.skipPermissions || false); + setProjectSortOrder(settings.projectSortOrder || 'name'); } else { // Set defaults setAllowedTools([]); setDisallowedTools([]); setSkipPermissions(false); + setProjectSortOrder('name'); } } catch (error) { console.error('Error loading tool settings:', error); @@ -63,6 +66,7 @@ function ToolsSettings({ isOpen, onClose }) { setAllowedTools([]); setDisallowedTools([]); setSkipPermissions(false); + setProjectSortOrder('name'); } }; @@ -75,6 +79,7 @@ function ToolsSettings({ isOpen, onClose }) { allowedTools, disallowedTools, skipPermissions, + projectSortOrder, lastUpdated: new Date().toISOString() }; @@ -212,6 +217,56 @@ function ToolsSettings({ isOpen, onClose }) { + {/* Project Sorting */} +
+
+ +

+ Project Sorting +

+
+
+
+
+ Choose how projects are sorted in the sidebar +
+
+ + +
+
+ {projectSortOrder === 'name' + ? 'Projects are sorted alphabetically by name' + : 'Projects are sorted by most recent session activity'} +
+
+
+
+ {/* Allowed Tools */}
From 1f3fe2df3ddc0a238588abe87e4aa539b9af11f0 Mon Sep 17 00:00:00 2001 From: Valics Lehel Date: Fri, 11 Jul 2025 23:14:07 +0300 Subject: [PATCH 2/5] feat: Add file metadata display with view modes - Added file size, permissions (rwx format), and modified date display - Implemented three view modes: simple, compact, and detailed - Added server-side file stats collection in getFileTree - View mode preference persisted in localStorage - Detailed view shows table-like layout with column headers - Compact view shows inline metadata - Simple view maintains original basic tree structure --- server/index.js | 32 +++++- src/components/FileTree.jsx | 214 +++++++++++++++++++++++++++++++++++- 2 files changed, 242 insertions(+), 4 deletions(-) diff --git a/server/index.js b/server/index.js index 3baeed8..650e216 100755 --- a/server/index.js +++ b/server/index.js @@ -820,6 +820,14 @@ app.get('*', (req, res) => { res.sendFile(path.join(__dirname, '../dist/index.html')); }); +// Helper function to convert permissions to rwx format +function permToRwx(perm) { + const r = perm & 4 ? 'r' : '-'; + const w = perm & 2 ? 'w' : '-'; + const x = perm & 1 ? 'x' : '-'; + return r + w + x; +} + async function getFileTree(dirPath, maxDepth = 3, currentDepth = 0, showHidden = true) { // Using fsPromises from import const items = []; @@ -836,12 +844,34 @@ async function getFileTree(dirPath, maxDepth = 3, currentDepth = 0, showHidden = entry.name === 'dist' || entry.name === 'build') continue; + const itemPath = path.join(dirPath, entry.name); const item = { name: entry.name, - path: path.join(dirPath, entry.name), + path: itemPath, type: entry.isDirectory() ? 'directory' : 'file' }; + // Get file stats for additional metadata + try { + const stats = await fsPromises.stat(itemPath); + item.size = stats.size; + item.modified = stats.mtime.toISOString(); + + // Convert permissions to rwx format + const mode = stats.mode; + const ownerPerm = (mode >> 6) & 7; + const groupPerm = (mode >> 3) & 7; + const otherPerm = mode & 7; + item.permissions = ((mode >> 6) & 7).toString() + ((mode >> 3) & 7).toString() + (mode & 7).toString(); + item.permissionsRwx = permToRwx(ownerPerm) + permToRwx(groupPerm) + permToRwx(otherPerm); + } catch (statError) { + // If stat fails, provide default values + item.size = 0; + item.modified = null; + item.permissions = '000'; + item.permissionsRwx = '---------'; + } + if (entry.isDirectory() && currentDepth < maxDepth) { // Recursively get subdirectories but limit depth try { diff --git a/src/components/FileTree.jsx b/src/components/FileTree.jsx index c2328db..1048bdb 100755 --- a/src/components/FileTree.jsx +++ b/src/components/FileTree.jsx @@ -1,7 +1,7 @@ import React, { useState, useEffect } from 'react'; import { ScrollArea } from './ui/scroll-area'; import { Button } from './ui/button'; -import { Folder, FolderOpen, File, FileText, FileCode } from 'lucide-react'; +import { Folder, FolderOpen, File, FileText, FileCode, List, TableProperties, Eye } from 'lucide-react'; import { cn } from '../lib/utils'; import CodeEditor from './CodeEditor'; import ImageViewer from './ImageViewer'; @@ -13,6 +13,7 @@ function FileTree({ selectedProject }) { const [expandedDirs, setExpandedDirs] = useState(new Set()); const [selectedFile, setSelectedFile] = useState(null); const [selectedImage, setSelectedImage] = useState(null); + const [viewMode, setViewMode] = useState('detailed'); // 'simple', 'detailed', 'compact' useEffect(() => { if (selectedProject) { @@ -20,6 +21,14 @@ function FileTree({ selectedProject }) { } }, [selectedProject]); + // Load view mode preference from localStorage + useEffect(() => { + const savedViewMode = localStorage.getItem('file-tree-view-mode'); + if (savedViewMode && ['simple', 'detailed', 'compact'].includes(savedViewMode)) { + setViewMode(savedViewMode); + } + }, []); + const fetchFiles = async () => { setLoading(true); try { @@ -52,6 +61,35 @@ function FileTree({ selectedProject }) { setExpandedDirs(newExpanded); }; + // Change view mode and save preference + const changeViewMode = (mode) => { + setViewMode(mode); + localStorage.setItem('file-tree-view-mode', mode); + }; + + // Format file size + const formatFileSize = (bytes) => { + if (!bytes || bytes === 0) return '0 B'; + const k = 1024; + const sizes = ['B', 'KB', 'MB', 'GB']; + const i = Math.floor(Math.log(bytes) / Math.log(k)); + return parseFloat((bytes / Math.pow(k, i)).toFixed(1)) + ' ' + sizes[i]; + }; + + // Format date as relative time + const formatRelativeTime = (date) => { + if (!date) return '-'; + const now = new Date(); + const past = new Date(date); + const diffInSeconds = Math.floor((now - past) / 1000); + + if (diffInSeconds < 60) return 'just now'; + if (diffInSeconds < 3600) return `${Math.floor(diffInSeconds / 60)} min ago`; + if (diffInSeconds < 86400) return `${Math.floor(diffInSeconds / 3600)} hours ago`; + if (diffInSeconds < 2592000) return `${Math.floor(diffInSeconds / 86400)} days ago`; + return past.toLocaleDateString(); + }; + const renderFileTree = (items, level = 0) => { return items.map((item) => (
@@ -135,6 +173,129 @@ function FileTree({ selectedProject }) { } }; + // Render detailed view with table-like layout + const renderDetailedView = (items, level = 0) => { + return items.map((item) => ( +
+
{ + if (item.type === 'directory') { + toggleDirectory(item.path); + } else if (isImageFile(item.name)) { + setSelectedImage({ + name: item.name, + path: item.path, + projectPath: selectedProject.path, + projectName: selectedProject.name + }); + } else { + setSelectedFile({ + name: item.name, + path: item.path, + projectPath: selectedProject.path, + projectName: selectedProject.name + }); + } + }} + > +
+ {item.type === 'directory' ? ( + expandedDirs.has(item.path) ? ( + + ) : ( + + ) + ) : ( + getFileIcon(item.name) + )} + + {item.name} + +
+
+ {item.type === 'file' ? formatFileSize(item.size) : '-'} +
+
+ {formatRelativeTime(item.modified)} +
+
+ {item.permissionsRwx || '-'} +
+
+ + {item.type === 'directory' && + expandedDirs.has(item.path) && + item.children && + renderDetailedView(item.children, level + 1)} +
+ )); + }; + + // Render compact view with inline details + const renderCompactView = (items, level = 0) => { + return items.map((item) => ( +
+
{ + if (item.type === 'directory') { + toggleDirectory(item.path); + } else if (isImageFile(item.name)) { + setSelectedImage({ + name: item.name, + path: item.path, + projectPath: selectedProject.path, + projectName: selectedProject.name + }); + } else { + setSelectedFile({ + name: item.name, + path: item.path, + projectPath: selectedProject.path, + projectName: selectedProject.name + }); + } + }} + > +
+ {item.type === 'directory' ? ( + expandedDirs.has(item.path) ? ( + + ) : ( + + ) + ) : ( + getFileIcon(item.name) + )} + + {item.name} + +
+
+ {item.type === 'file' && ( + <> + {formatFileSize(item.size)} + {item.permissionsRwx} + + )} +
+
+ + {item.type === 'directory' && + expandedDirs.has(item.path) && + item.children && + renderCompactView(item.children, level + 1)} +
+ )); + }; + if (loading) { return (
@@ -147,6 +308,51 @@ function FileTree({ selectedProject }) { return (
+ {/* View Mode Toggle */} +
+

Files

+
+ + + +
+
+ + {/* Column Headers for Detailed View */} + {viewMode === 'detailed' && files.length > 0 && ( +
+
+
Name
+
Size
+
Modified
+
Permissions
+
+
+ )} {files.length === 0 ? ( @@ -160,8 +366,10 @@ function FileTree({ selectedProject }) {

) : ( -
- {renderFileTree(files)} +
+ {viewMode === 'simple' && renderFileTree(files)} + {viewMode === 'compact' && renderCompactView(files)} + {viewMode === 'detailed' && renderDetailedView(files)}
)} From 02a296739d6d8f0bdda0254c2b4f2ee030acc4c2 Mon Sep 17 00:00:00 2001 From: Valics Lehel Date: Sat, 12 Jul 2025 14:17:56 +0300 Subject: [PATCH 3/5] fix: Address project sorting feedback - Sort by user-defined displayName instead of path/folder name - Move project sorting under Appearance section - Replace large toggle buttons with compact dropdown - Use clearer labels: "Alphabetical" and "Recent Activity" - Projects now sort by custom names when available --- file-metadata-issue.md | 85 ++++++++++++++++++++++++++++++++ src/components/Sidebar.jsx | 6 ++- src/components/ToolsSettings.jsx | 74 +++++++++------------------ 3 files changed, 112 insertions(+), 53 deletions(-) create mode 100644 file-metadata-issue.md diff --git a/file-metadata-issue.md b/file-metadata-issue.md new file mode 100644 index 0000000..bf479c3 --- /dev/null +++ b/file-metadata-issue.md @@ -0,0 +1,85 @@ +# Issue: Add File Metadata Display with Multiple View Modes + +## Feature Description + +This feature enhances the file tree display by adding file metadata and multiple view modes for better file browsing experience. + +### What's New: +- **File metadata display**: Size, permissions (rwx format), and last modified date +- **Three view modes**: + - **Simple**: Original tree view (file names only) + - **Compact**: Inline metadata display + - **Detailed**: Table-like layout with column headers +- **Persistent preferences**: View mode selection saved in localStorage +- **Server-side enhancements**: File stats collection in the API + +## Screenshots/Demo + +### Detailed View +Shows files in a table format with columns: +- Name | Size | Modified | Permissions + +### Compact View +Shows files with inline metadata: +- filename.js 2.5 KB rw-r--r-- + +### Simple View +Original tree structure with just file names and icons + +## How to Test + +### Option 1: Test from my feature branch +```bash +# Clone and checkout the feature branch +git clone https://github.com/lvalics/claudecodeui.git +cd claudecodeui +git checkout feature/file-permissions + +# Install and run +npm install +npm run dev +``` + +### Option 2: View the implementation +- **Client changes**: [src/components/FileTree.jsx](https://github.com/lvalics/claudecodeui/blob/feature/file-permissions/src/components/FileTree.jsx) +- **Server changes**: [server/index.js](https://github.com/lvalics/claudecodeui/blob/feature/file-permissions/server/index.js) (see `getFileTree` function) + +### Option 3: Try the live demo (if deployed) +[If you have a deployment URL, add it here] + +## Implementation Details + +### Client-side changes (FileTree.jsx): +- Added view mode state and localStorage persistence +- Created three render functions: `renderFileTree`, `renderCompactView`, `renderDetailedView` +- Added helper functions: `formatFileSize`, `formatRelativeTime` +- Added view mode toggle buttons in the header + +### Server-side changes (server/index.js): +- Enhanced `getFileTree` function to collect file stats +- Added `permToRwx` helper function for permission formatting +- Returns additional fields: `size`, `modified`, `permissions`, `permissionsRwx` + +## Benefits +1. **Better file overview**: See file sizes and permissions at a glance +2. **Flexible viewing**: Switch between simple and detailed views based on needs +3. **Permission visibility**: Quickly identify file access rights +4. **Recent activity**: See when files were last modified + +## Compatibility +- No breaking changes to existing functionality +- Gracefully handles missing metadata with fallback values +- Works on all platforms (permissions display adapted for Windows) + +## Related PRs +- Feature branch PR: #[PR_NUMBER] (when created) +- Individual feature PR: https://github.com/lvalics/claudecodeui/pull/new/feature/file-permissions + +## Testing Checklist +- [ ] File tree loads correctly in all three view modes +- [ ] View mode preference persists after page reload +- [ ] File sizes display correctly (B, KB, MB, GB) +- [ ] Permissions show in rwx format (e.g., rw-r--r--) +- [ ] Modified dates show as relative time +- [ ] Clicking files opens them correctly in all view modes +- [ ] Directory expansion works in all view modes \ No newline at end of file diff --git a/src/components/Sidebar.jsx b/src/components/Sidebar.jsx index 598b093..9528919 100755 --- a/src/components/Sidebar.jsx +++ b/src/components/Sidebar.jsx @@ -353,8 +353,10 @@ function Sidebar({ // Sort by most recent activity (descending) return getProjectLastActivity(b) - getProjectLastActivity(a); } else { - // Sort by name (ascending) - return a.name.localeCompare(b.name); + // Sort by display name (user-defined) or fallback to name (ascending) + const nameA = a.displayName || a.name; + const nameB = b.displayName || b.name; + return nameA.localeCompare(nameB); } }); diff --git a/src/components/ToolsSettings.jsx b/src/components/ToolsSettings.jsx index 7e5295a..109979e 100755 --- a/src/components/ToolsSettings.jsx +++ b/src/components/ToolsSettings.jsx @@ -3,7 +3,7 @@ import { Button } from './ui/button'; import { Input } from './ui/input'; import { ScrollArea } from './ui/scroll-area'; import { Badge } from './ui/badge'; -import { X, Plus, Settings, Shield, AlertTriangle, Moon, Sun, FolderOpen, SortAsc } from 'lucide-react'; +import { X, Plus, Settings, Shield, AlertTriangle, Moon, Sun } from 'lucide-react'; import { useTheme } from '../contexts/ThemeContext'; function ToolsSettings({ isOpen, onClose }) { @@ -186,6 +186,28 @@ function ToolsSettings({ isOpen, onClose }) {
+ + {/* Project Sorting - Moved under Appearance */} +
+
+
+
+ Project Sorting +
+
+ How projects are ordered in the sidebar +
+
+ +
+
@@ -217,56 +239,6 @@ function ToolsSettings({ isOpen, onClose }) {
- {/* Project Sorting */} -
-
- -

- Project Sorting -

-
-
-
-
- Choose how projects are sorted in the sidebar -
-
- - -
-
- {projectSortOrder === 'name' - ? 'Projects are sorted alphabetically by name' - : 'Projects are sorted by most recent session activity'} -
-
-
-
- {/* Allowed Tools */}
From 211a3c4557cef0da031d23c7944a0514e0655692 Mon Sep 17 00:00:00 2001 From: viper151 Date: Sat, 12 Jul 2025 13:28:41 +0200 Subject: [PATCH 4/5] Delete file-metadata-issue.md --- file-metadata-issue.md | 85 ------------------------------------------ 1 file changed, 85 deletions(-) delete mode 100644 file-metadata-issue.md diff --git a/file-metadata-issue.md b/file-metadata-issue.md deleted file mode 100644 index bf479c3..0000000 --- a/file-metadata-issue.md +++ /dev/null @@ -1,85 +0,0 @@ -# Issue: Add File Metadata Display with Multiple View Modes - -## Feature Description - -This feature enhances the file tree display by adding file metadata and multiple view modes for better file browsing experience. - -### What's New: -- **File metadata display**: Size, permissions (rwx format), and last modified date -- **Three view modes**: - - **Simple**: Original tree view (file names only) - - **Compact**: Inline metadata display - - **Detailed**: Table-like layout with column headers -- **Persistent preferences**: View mode selection saved in localStorage -- **Server-side enhancements**: File stats collection in the API - -## Screenshots/Demo - -### Detailed View -Shows files in a table format with columns: -- Name | Size | Modified | Permissions - -### Compact View -Shows files with inline metadata: -- filename.js 2.5 KB rw-r--r-- - -### Simple View -Original tree structure with just file names and icons - -## How to Test - -### Option 1: Test from my feature branch -```bash -# Clone and checkout the feature branch -git clone https://github.com/lvalics/claudecodeui.git -cd claudecodeui -git checkout feature/file-permissions - -# Install and run -npm install -npm run dev -``` - -### Option 2: View the implementation -- **Client changes**: [src/components/FileTree.jsx](https://github.com/lvalics/claudecodeui/blob/feature/file-permissions/src/components/FileTree.jsx) -- **Server changes**: [server/index.js](https://github.com/lvalics/claudecodeui/blob/feature/file-permissions/server/index.js) (see `getFileTree` function) - -### Option 3: Try the live demo (if deployed) -[If you have a deployment URL, add it here] - -## Implementation Details - -### Client-side changes (FileTree.jsx): -- Added view mode state and localStorage persistence -- Created three render functions: `renderFileTree`, `renderCompactView`, `renderDetailedView` -- Added helper functions: `formatFileSize`, `formatRelativeTime` -- Added view mode toggle buttons in the header - -### Server-side changes (server/index.js): -- Enhanced `getFileTree` function to collect file stats -- Added `permToRwx` helper function for permission formatting -- Returns additional fields: `size`, `modified`, `permissions`, `permissionsRwx` - -## Benefits -1. **Better file overview**: See file sizes and permissions at a glance -2. **Flexible viewing**: Switch between simple and detailed views based on needs -3. **Permission visibility**: Quickly identify file access rights -4. **Recent activity**: See when files were last modified - -## Compatibility -- No breaking changes to existing functionality -- Gracefully handles missing metadata with fallback values -- Works on all platforms (permissions display adapted for Windows) - -## Related PRs -- Feature branch PR: #[PR_NUMBER] (when created) -- Individual feature PR: https://github.com/lvalics/claudecodeui/pull/new/feature/file-permissions - -## Testing Checklist -- [ ] File tree loads correctly in all three view modes -- [ ] View mode preference persists after page reload -- [ ] File sizes display correctly (B, KB, MB, GB) -- [ ] Permissions show in rwx format (e.g., rw-r--r--) -- [ ] Modified dates show as relative time -- [ ] Clicking files opens them correctly in all view modes -- [ ] Directory expansion works in all view modes \ No newline at end of file From c6c11c236ca1003050597e44b9afe540d2419779 Mon Sep 17 00:00:00 2001 From: viper151 Date: Sat, 12 Jul 2025 13:33:26 +0200 Subject: [PATCH 5/5] Update ToolsSettings.jsx --- src/components/ToolsSettings.jsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/ToolsSettings.jsx b/src/components/ToolsSettings.jsx index 109979e..ff98a3e 100755 --- a/src/components/ToolsSettings.jsx +++ b/src/components/ToolsSettings.jsx @@ -201,7 +201,7 @@ function ToolsSettings({ isOpen, onClose }) {