mirror of
https://github.com/siteboon/claudecodeui.git
synced 2026-01-25 02:47:31 +00:00
Merge branch 'main' into feat/add-i18n
This commit is contained in:
35
src/App.jsx
35
src/App.jsx
@@ -18,7 +18,7 @@
|
||||
* Handles both existing sessions (with real IDs) and new sessions (with temporary IDs).
|
||||
*/
|
||||
|
||||
import React, { useState, useEffect, useCallback } from 'react';
|
||||
import React, { useState, useEffect, useCallback, useRef } from 'react';
|
||||
import { BrowserRouter as Router, Routes, Route, useNavigate, useParams } from 'react-router-dom';
|
||||
import { Settings as SettingsIcon, Sparkles } from 'lucide-react';
|
||||
import Sidebar from './components/Sidebar';
|
||||
@@ -55,6 +55,7 @@ function AppContent() {
|
||||
const [isMobile, setIsMobile] = useState(false);
|
||||
const [sidebarOpen, setSidebarOpen] = useState(false);
|
||||
const [isLoadingProjects, setIsLoadingProjects] = useState(true);
|
||||
const [loadingProgress, setLoadingProgress] = useState(null); // { phase, current, total, currentProject }
|
||||
const [isInputFocused, setIsInputFocused] = useState(false);
|
||||
const [showSettings, setShowSettings] = useState(false);
|
||||
const [settingsInitialTab, setSettingsInitialTab] = useState('agents');
|
||||
@@ -80,7 +81,10 @@ function AppContent() {
|
||||
const [externalMessageUpdate, setExternalMessageUpdate] = useState(0);
|
||||
|
||||
const { ws, sendMessage, messages } = useWebSocketContext();
|
||||
|
||||
|
||||
// Ref to track loading progress timeout for cleanup
|
||||
const loadingProgressTimeoutRef = useRef(null);
|
||||
|
||||
// Detect if running as PWA
|
||||
const [isPWA, setIsPWA] = useState(false);
|
||||
|
||||
@@ -172,7 +176,23 @@ function AppContent() {
|
||||
useEffect(() => {
|
||||
if (messages.length > 0) {
|
||||
const latestMessage = messages[messages.length - 1];
|
||||
|
||||
|
||||
// Handle loading progress updates
|
||||
if (latestMessage.type === 'loading_progress') {
|
||||
if (loadingProgressTimeoutRef.current) {
|
||||
clearTimeout(loadingProgressTimeoutRef.current);
|
||||
loadingProgressTimeoutRef.current = null;
|
||||
}
|
||||
setLoadingProgress(latestMessage);
|
||||
if (latestMessage.phase === 'complete') {
|
||||
loadingProgressTimeoutRef.current = setTimeout(() => {
|
||||
setLoadingProgress(null);
|
||||
loadingProgressTimeoutRef.current = null;
|
||||
}, 500);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (latestMessage.type === 'projects_updated') {
|
||||
|
||||
// External Session Update Detection: Check if the changed file is the current session's JSONL
|
||||
@@ -249,6 +269,13 @@ function AppContent() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return () => {
|
||||
if (loadingProgressTimeoutRef.current) {
|
||||
clearTimeout(loadingProgressTimeoutRef.current);
|
||||
loadingProgressTimeoutRef.current = null;
|
||||
}
|
||||
};
|
||||
}, [messages, selectedProject, selectedSession, activeSessions]);
|
||||
|
||||
const fetchProjects = async () => {
|
||||
@@ -767,6 +794,7 @@ function AppContent() {
|
||||
onSessionDelete={handleSessionDelete}
|
||||
onProjectDelete={handleProjectDelete}
|
||||
isLoading={isLoadingProjects}
|
||||
loadingProgress={loadingProgress}
|
||||
onRefresh={handleSidebarRefresh}
|
||||
onShowSettings={() => setShowSettings(true)}
|
||||
updateAvailable={updateAvailable}
|
||||
@@ -861,6 +889,7 @@ function AppContent() {
|
||||
onSessionDelete={handleSessionDelete}
|
||||
onProjectDelete={handleProjectDelete}
|
||||
isLoading={isLoadingProjects}
|
||||
loadingProgress={loadingProgress}
|
||||
onRefresh={handleSidebarRefresh}
|
||||
onShowSettings={() => setShowSettings(true)}
|
||||
updateAvailable={updateAvailable}
|
||||
|
||||
@@ -53,6 +53,7 @@ function Sidebar({
|
||||
onSessionDelete,
|
||||
onProjectDelete,
|
||||
isLoading,
|
||||
loadingProgress,
|
||||
onRefresh,
|
||||
onShowSettings,
|
||||
updateAvailable,
|
||||
@@ -668,6 +669,29 @@ function Sidebar({
|
||||
<p className="text-sm text-muted-foreground">
|
||||
{t('projects.fetchingProjects')}
|
||||
</p>
|
||||
<h3 className="text-base font-medium text-foreground mb-2 md:mb-1">Loading projects...</h3>
|
||||
{loadingProgress && loadingProgress.total > 0 ? (
|
||||
<div className="space-y-2">
|
||||
<div className="w-full bg-muted rounded-full h-2 overflow-hidden">
|
||||
<div
|
||||
className="bg-primary h-full transition-all duration-300 ease-out"
|
||||
style={{ width: `${(loadingProgress.current / loadingProgress.total) * 100}%` }}
|
||||
/>
|
||||
</div>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
{loadingProgress.current}/{loadingProgress.total} projects
|
||||
</p>
|
||||
{loadingProgress.currentProject && (
|
||||
<p className="text-xs text-muted-foreground/70 truncate max-w-[200px] mx-auto" title={loadingProgress.currentProject}>
|
||||
{loadingProgress.currentProject.split('-').slice(-2).join('/')}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Fetching your Claude projects and sessions
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
) : projects.length === 0 ? (
|
||||
<div className="text-center py-12 md:py-8 px-4">
|
||||
@@ -1168,11 +1192,11 @@ function Sidebar({
|
||||
{formatTimeAgo(sessionTime, currentTime, t)}
|
||||
</span>
|
||||
{messageCount > 0 && (
|
||||
<Badge variant="secondary" className="text-xs px-1 py-0 ml-auto">
|
||||
<Badge variant="secondary" className="text-xs px-1 py-0 ml-auto group-hover:opacity-0 transition-opacity">
|
||||
{messageCount}
|
||||
</Badge>
|
||||
)}
|
||||
<span className="ml-1 opacity-70">
|
||||
<span className="ml-1 opacity-70 group-hover:opacity-0 transition-opacity">
|
||||
{isCursorSession ? (
|
||||
<CursorLogo className="w-3 h-3" />
|
||||
) : isCodexSession ? (
|
||||
|
||||
Reference in New Issue
Block a user