增加直接创建会话

This commit is contained in:
2025-10-16 11:34:10 +08:00
parent b2be1ac401
commit 9d30fd0dac
13 changed files with 818 additions and 25 deletions

View File

@@ -354,6 +354,14 @@ export const ClaudeCodeSession: React.FC<ClaudeCodeSessionProps> = ({
queuedPromptsRef.current = queuedPrompts;
}, [queuedPrompts]);
// 当父组件通过智能会话或外部导航注入项目路径时,确保初次渲染即可进入工作区而非停留在创建页
useEffect(() => {
if (!session && initialProjectPath && projectPath.trim().length === 0) {
setProjectPath(initialProjectPath);
setError(null);
}
}, [initialProjectPath, projectPath, session]);
// Get effective session info (from prop or extracted) - use useMemo to ensure it updates
const effectiveSession = useMemo(() => {
if (session) return session;
@@ -474,6 +482,22 @@ export const ClaudeCodeSession: React.FC<ClaudeCodeSessionProps> = ({
}
}, [projectPath, tabId, session, updateTab]);
// Auto-start file monitoring when project path is available
useEffect(() => {
if (projectPath && !isFileWatching) {
// Auto-start file monitoring for smart sessions or when project path is set
const timeoutId = setTimeout(async () => {
try {
await startFileWatching();
} catch (error) {
console.warn('[ClaudeCodeSession] Failed to auto-start file monitoring:', error);
}
}, 500); // Small delay to ensure component is fully mounted
return () => clearTimeout(timeoutId);
}
}, [projectPath, isFileWatching, startFileWatching]);
// 滚动到顶部
const scrollToTop = useCallback(() => {
if (parentRef.current) {
@@ -1527,7 +1551,7 @@ export const ClaudeCodeSession: React.FC<ClaudeCodeSessionProps> = ({
</div>
);
const projectPathInput = !session && (
const projectPathInput = !session && !projectPath && (
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}

View File

@@ -196,6 +196,7 @@ const TabPanel: React.FC<TabPanelProps> = ({ tab, isActive }) => {
case 'chat':
return (
<ClaudeCodeSession
key={`${tab.id}-${tab.initialProjectPath || 'no-path'}`} // Force re-render when path changes
session={tab.sessionData} // Pass the full session object if available
initialProjectPath={tab.initialProjectPath || tab.sessionId}
tabId={tab.id} // Pass tabId for state synchronization
@@ -392,6 +393,24 @@ export const TabContent: React.FC = () => {
}
};
const handleCreateSmartSessionTab = (event: CustomEvent) => {
const { tabId, sessionData } = event.detail;
console.log('[TabContent] Handling create-smart-session-tab:', { tabId, sessionData });
// Update the existing tab with smart session data and switch immediately
updateTab(tabId, {
type: 'chat',
title: sessionData.display_name || 'Smart Session',
initialProjectPath: sessionData.project_path,
sessionData: null, // No existing session, this is a new session workspace
});
// Force immediate tab switch without delay
setTimeout(() => {
window.dispatchEvent(new CustomEvent('switch-to-tab', { detail: { tabId } }));
}, 0);
};
window.addEventListener('open-session-in-tab', handleOpenSessionInTab as EventListener);
window.addEventListener('open-claude-file', handleOpenClaudeFile as EventListener);
window.addEventListener('open-agent-execution', handleOpenAgentExecution as EventListener);
@@ -399,6 +418,7 @@ export const TabContent: React.FC = () => {
window.addEventListener('open-import-agent-tab', handleOpenImportAgentTab);
window.addEventListener('close-tab', handleCloseTab as EventListener);
window.addEventListener('claude-session-selected', handleClaudeSessionSelected as EventListener);
window.addEventListener('create-smart-session-tab', handleCreateSmartSessionTab as EventListener);
return () => {
window.removeEventListener('open-session-in-tab', handleOpenSessionInTab as EventListener);
window.removeEventListener('open-claude-file', handleOpenClaudeFile as EventListener);
@@ -407,6 +427,7 @@ export const TabContent: React.FC = () => {
window.removeEventListener('open-import-agent-tab', handleOpenImportAgentTab);
window.removeEventListener('close-tab', handleCloseTab as EventListener);
window.removeEventListener('claude-session-selected', handleClaudeSessionSelected as EventListener);
window.removeEventListener('create-smart-session-tab', handleCreateSmartSessionTab as EventListener);
};
}, [createChatTab, findTabBySessionId, createClaudeFileTab, createAgentExecutionTab, createCreateAgentTab, createImportAgentTab, closeTab, updateTab]);

View File

@@ -221,16 +221,16 @@ export const TabManager: React.FC<TabManagerProps> = ({ className }) => {
if (session && canAddTab()) {
// Create a new chat tab with the session data
const tabId = createChatTab();
// Update the tab with session data
setTimeout(() => {
updateTab(tabId, {
type: 'chat',
title: session.project_path.split('/').pop() || 'Session',
sessionId: session.id,
sessionData: session,
initialProjectPath: projectPath || session.project_path,
});
}, 100);
// Update the tab with session data immediately
updateTab(tabId, {
type: 'chat',
title: session.project_path.split('/').pop() || 'Session',
sessionId: session.id,
sessionData: session,
initialProjectPath: projectPath || session.project_path,
});
// Switch to the new tab immediately
switchToTab(tabId);
}
};

View File

@@ -1,16 +1,19 @@
import { motion } from "framer-motion";
import { Bot, FolderCode, BarChart, ServerCog, FileText, Settings, Network, Router } from "lucide-react";
import { Bot, FolderCode, BarChart, ServerCog, FileText, Settings, Network, Router, Zap, FolderOpen, Loader2 } from "lucide-react";
import { useTranslation } from "@/hooks/useTranslation";
import { Button } from "@/components/ui/button";
import { ClaudiaLogoMinimal } from "@/components/ClaudiaLogo";
import { useState } from "react";
interface WelcomePageProps {
onNavigate: (view: string) => void;
onNewSession: () => void;
onSmartQuickStart?: () => void;
}
export function WelcomePage({ onNavigate, onNewSession }: WelcomePageProps) {
export function WelcomePage({ onNavigate, onNewSession, onSmartQuickStart }: WelcomePageProps) {
const { t } = useTranslation();
const [isCreatingSmartSession, setIsCreatingSmartSession] = useState(false);
const mainFeatures = [
{
@@ -98,6 +101,17 @@ export function WelcomePage({ onNavigate, onNewSession }: WelcomePageProps) {
onNewSession();
};
const handleSmartQuickStartClick = async () => {
if (!onSmartQuickStart) return;
setIsCreatingSmartSession(true);
try {
await onSmartQuickStart();
} finally {
setIsCreatingSmartSession(false);
}
};
return (
<div className="flex items-center justify-center min-h-screen bg-background overflow-hidden">
<div className="w-full max-w-6xl px-8 -mt-20">
@@ -191,7 +205,7 @@ export function WelcomePage({ onNavigate, onNewSession }: WelcomePageProps) {
))}
</div>
{/* Quick Action Button */}
{/* Quick Action Buttons */}
<motion.div
initial={{ opacity: 0, scale: 0.9 }}
animate={{ opacity: 1, scale: 1 }}
@@ -201,21 +215,47 @@ export function WelcomePage({ onNavigate, onNewSession }: WelcomePageProps) {
type: "spring",
stiffness: 100
}}
className="flex justify-center"
className="flex justify-center gap-4"
>
{/* 智能快速开始 - 新功能 */}
{onSmartQuickStart && (
<Button
size="lg"
className="relative px-8 py-6 text-lg font-semibold bg-orange-500 hover:bg-orange-600 text-white border-0 shadow-2xl hover:shadow-orange-500/25 transition-all duration-300 hover:scale-105 rounded-2xl group overflow-hidden"
onClick={handleSmartQuickStartClick}
disabled={isCreatingSmartSession}
>
{/* Shimmer effect on button */}
<div className="absolute inset-0 opacity-0 group-hover:opacity-100 transition-opacity duration-300">
<div className="absolute inset-0 bg-gradient-to-r from-transparent via-white/20 to-transparent animate-shimmer" />
</div>
<span className="relative z-10 flex items-center gap-2">
{isCreatingSmartSession ? (
<>
<Loader2 className="w-5 h-5 animate-spin" />
{t("welcome.creatingSmartSession")}
</>
) : (
<>
<Zap className="w-5 h-5" />
{t("welcome.smartQuickStart")}
</>
)}
</span>
</Button>
)}
{/* 传统快速开始 - 保持原功能 */}
<Button
size="lg"
className="relative px-10 py-7 text-lg font-semibold bg-orange-500 hover:bg-orange-600 text-white border-0 shadow-2xl hover:shadow-orange-500/25 transition-all duration-300 hover:scale-105 rounded-2xl group overflow-hidden"
variant="outline"
className="relative px-8 py-6 text-lg font-semibold border-2 border-orange-500 text-orange-500 hover:bg-orange-500 hover:text-white transition-all duration-300 hover:scale-105 rounded-2xl group"
onClick={handleButtonClick}
>
{/* Shimmer effect on button */}
<div className="absolute inset-0 opacity-0 group-hover:opacity-100 transition-opacity duration-300">
<div className="absolute inset-0 bg-gradient-to-r from-transparent via-white/20 to-transparent animate-shimmer" />
</div>
<span className="relative z-10 flex items-center gap-2">
<span className="text-2xl">+</span>
{t("welcome.quickStartSession")}
<FolderOpen className="w-5 h-5" />
{t("welcome.choosePathStart")}
</span>
</Button>
</motion.div>