diff --git a/package-lock.json b/package-lock.json index 8158c40..ed6cdfc 100755 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "claude-code-ui", - "version": "1.1.4", + "version": "1.2.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "claude-code-ui", - "version": "1.1.4", + "version": "1.2.0", "license": "MIT", "dependencies": { "@anthropic-ai/claude-code": "^1.0.24", diff --git a/src/components/ChatInterface.jsx b/src/components/ChatInterface.jsx index b3238fb..b0caae7 100755 --- a/src/components/ChatInterface.jsx +++ b/src/components/ChatInterface.jsx @@ -1597,6 +1597,14 @@ function ChatInterface({ selectedProject, selectedSession, ws, sendMessage, mess } }, []); // Only run once on mount + // Reset textarea height when input is cleared programmatically + useEffect(() => { + if (textareaRef.current && !input.trim()) { + textareaRef.current.style.height = 'auto'; + setIsTextareaExpanded(false); + } + }, [input]); + const handleTranscript = useCallback((text) => { if (text.trim()) { setInput(prevInput => { @@ -1689,6 +1697,12 @@ function ChatInterface({ selectedProject, selectedSession, ws, sendMessage, mess setInput(''); setIsTextareaExpanded(false); + + // Reset textarea height to minimal state + if (textareaRef.current) { + textareaRef.current.style.height = 'auto'; + } + // Clear the saved draft since message was sent if (selectedProject) { localStorage.removeItem(`draft_input_${selectedProject.name}`); @@ -1776,8 +1790,15 @@ function ChatInterface({ selectedProject, selectedSession, ws, sendMessage, mess }; const handleInputChange = (e) => { - setInput(e.target.value); + const newValue = e.target.value; + setInput(newValue); setCursorPosition(e.target.selectionStart); + + // Handle height reset when input becomes empty + if (!newValue.trim()) { + e.target.style.height = 'auto'; + setIsTextareaExpanded(false); + } }; const handleTextareaClick = (e) => { diff --git a/src/components/Sidebar.jsx b/src/components/Sidebar.jsx index 9528919..5e9c566 100755 --- a/src/components/Sidebar.jsx +++ b/src/components/Sidebar.jsx @@ -3,7 +3,7 @@ import { ScrollArea } from './ui/scroll-area'; import { Button } from './ui/button'; import { Badge } from './ui/badge'; import { Input } from './ui/input'; -import { FolderOpen, Folder, Plus, MessageSquare, Clock, ChevronDown, ChevronRight, Edit3, Check, X, Trash2, Settings, FolderPlus, RefreshCw, Sparkles, Edit2 } from 'lucide-react'; +import { FolderOpen, Folder, Plus, MessageSquare, Clock, ChevronDown, ChevronRight, Edit3, Check, X, Trash2, Settings, FolderPlus, RefreshCw, Sparkles, Edit2, Star } from 'lucide-react'; import { cn } from '../lib/utils'; import ClaudeLogo from './ClaudeLogo'; import { api } from '../utils/api'; @@ -66,6 +66,17 @@ function Sidebar({ const [editingSession, setEditingSession] = useState(null); const [editingSessionName, setEditingSessionName] = useState(''); const [generatingSummary, setGeneratingSummary] = useState({}); + + // Starred projects state - persisted in localStorage + const [starredProjects, setStarredProjects] = useState(() => { + try { + const saved = localStorage.getItem('starredProjects'); + return saved ? new Set(JSON.parse(saved)) : new Set(); + } catch (error) { + console.error('Error loading starred projects:', error); + return new Set(); + } + }); // Touch handler to prevent double-tap issues on iPad (only for buttons, not scroll areas) const handleTouchClick = (callback) => { @@ -164,6 +175,37 @@ function Sidebar({ setExpandedProjects(newExpanded); }; + // Starred projects utility functions + const toggleStarProject = (projectName) => { + const newStarred = new Set(starredProjects); + if (newStarred.has(projectName)) { + newStarred.delete(projectName); + } else { + newStarred.add(projectName); + } + setStarredProjects(newStarred); + + // Persist to localStorage + try { + localStorage.setItem('starredProjects', JSON.stringify([...newStarred])); + } catch (error) { + console.error('Error saving starred projects:', error); + } + }; + + const isProjectStarred = (projectName) => { + return starredProjects.has(projectName); + }; + + // Sort projects to show starred ones first + const sortedProjects = [...projects].sort((a, b) => { + const aStarred = isProjectStarred(a.name); + const bStarred = isProjectStarred(b.name); + + if (aStarred && !bStarred) return -1; + if (!aStarred && bStarred) return 1; + return 0; // Keep original order for same star status + }); const startEditing = (project) => { setEditingProject(project.name); @@ -571,6 +613,7 @@ function Sidebar({ sortedProjects.map((project) => { const isExpanded = expandedProjects.has(project.name); const isSelected = selectedProject?.name === project.name; + const isStarred = isProjectStarred(project.name); return (
@@ -581,7 +624,8 @@ function Sidebar({
{ // On mobile, just toggle the folder - don't select the project @@ -663,6 +707,28 @@ function Sidebar({ ) : ( <> + {/* Star button */} + {getAllSessions(project).length === 0 && (