Merge branch 'main' into feat/add-i18n

This commit is contained in:
Haileyesus Dessie
2026-01-21 17:16:48 +03:00
committed by GitHub
4 changed files with 141 additions and 18 deletions

View File

@@ -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}

View File

@@ -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 ? (