修复快速开始新对话以及点击项目无法跳转
This commit is contained in:
@@ -32,6 +32,7 @@ import { Popover } from "@/components/ui/popover";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { api, type Session } from "@/lib/api";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { useTabState } from "@/hooks/useTabState";
|
||||
import { open } from "@tauri-apps/plugin-dialog";
|
||||
import { listen, type UnlistenFn } from "@tauri-apps/api/event";
|
||||
import { StreamMessage } from "./StreamMessage";
|
||||
@@ -77,6 +78,10 @@ interface ClaudeCodeSessionProps {
|
||||
* Initial project path (for new sessions)
|
||||
*/
|
||||
initialProjectPath?: string;
|
||||
/**
|
||||
* Tab ID (for syncing state back to tab)
|
||||
*/
|
||||
tabId?: string;
|
||||
/**
|
||||
* Callback to go back
|
||||
*/
|
||||
@@ -104,18 +109,20 @@ interface ClaudeCodeSessionProps {
|
||||
export const ClaudeCodeSession: React.FC<ClaudeCodeSessionProps> = ({
|
||||
session,
|
||||
initialProjectPath = "",
|
||||
tabId,
|
||||
onBack,
|
||||
onProjectSettings,
|
||||
className,
|
||||
onStreamingChange,
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const { updateTab } = useTabState();
|
||||
const layoutManager = useLayoutManager(initialProjectPath || session?.project_path);
|
||||
const {
|
||||
layout,
|
||||
breakpoints,
|
||||
toggleFileExplorer,
|
||||
toggleGitPanel,
|
||||
const {
|
||||
layout,
|
||||
breakpoints,
|
||||
toggleFileExplorer,
|
||||
toggleGitPanel,
|
||||
toggleTimeline,
|
||||
setPanelWidth,
|
||||
setSplitPosition: setLayoutSplitPosition,
|
||||
@@ -128,7 +135,7 @@ export const ClaudeCodeSession: React.FC<ClaudeCodeSessionProps> = ({
|
||||
closeTerminal,
|
||||
toggleTerminalMaximize
|
||||
} = layoutManager;
|
||||
|
||||
|
||||
const [projectPath, setProjectPath] = useState(initialProjectPath || session?.project_path || "");
|
||||
const [messages, setMessages] = useState<ClaudeStreamMessage[]>([]);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
@@ -466,7 +473,19 @@ export const ClaudeCodeSession: React.FC<ClaudeCodeSessionProps> = ({
|
||||
useEffect(() => {
|
||||
onStreamingChange?.(isLoading, claudeSessionId);
|
||||
}, [isLoading, claudeSessionId, onStreamingChange]);
|
||||
|
||||
|
||||
// Sync projectPath to tab when it changes (for persistence)
|
||||
useEffect(() => {
|
||||
if (tabId && projectPath && !session) {
|
||||
// Only update for new sessions (not resumed sessions)
|
||||
// This ensures the path is saved when user selects/enters it
|
||||
console.log('[ClaudeCodeSession] Syncing projectPath to tab:', { tabId, projectPath });
|
||||
updateTab(tabId, {
|
||||
initialProjectPath: projectPath
|
||||
});
|
||||
}
|
||||
}, [projectPath, tabId, session, updateTab]);
|
||||
|
||||
// 滚动到顶部
|
||||
const scrollToTop = useCallback(() => {
|
||||
if (parentRef.current) {
|
||||
|
||||
@@ -3,12 +3,11 @@ import { motion, AnimatePresence } from 'framer-motion';
|
||||
import { useTabState } from '@/hooks/useTabState';
|
||||
import { useScreenTracking } from '@/hooks/useAnalytics';
|
||||
import { Tab } from '@/contexts/TabContext';
|
||||
import { Loader2, Plus } from 'lucide-react';
|
||||
import { Loader2 } from 'lucide-react';
|
||||
import { api, type Project, type Session, type ClaudeMdFile } from '@/lib/api';
|
||||
import { ProjectList } from '@/components/ProjectList';
|
||||
import { SessionList } from '@/components/SessionList';
|
||||
import { RunningClaudeSessions } from '@/components/RunningClaudeSessions';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { useTranslation } from '@/hooks/useTranslation';
|
||||
|
||||
// Lazy load heavy components
|
||||
@@ -162,36 +161,20 @@ const TabPanel: React.FC<TabPanelProps> = ({ tab, isActive }) => {
|
||||
animate={{ opacity: 1, x: 0 }}
|
||||
exit={{ opacity: 0, x: 20 }}
|
||||
transition={{ duration: 0.3 }}
|
||||
className="space-y-6"
|
||||
>
|
||||
{/* New session button at the top */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.5 }}
|
||||
className="mb-4"
|
||||
>
|
||||
<Button
|
||||
onClick={handleNewSession}
|
||||
size="default"
|
||||
className="w-full max-w-md"
|
||||
>
|
||||
<Plus className="mr-2 h-4 w-4" />
|
||||
{t('newClaudeCodeSession')}
|
||||
</Button>
|
||||
</motion.div>
|
||||
|
||||
{/* Running Claude Sessions */}
|
||||
{/* Running Claude Sessions - moved before project list */}
|
||||
<RunningClaudeSessions />
|
||||
|
||||
{/* Project list */}
|
||||
{/* Project list - now includes new session button and search */}
|
||||
{projects.length > 0 ? (
|
||||
<ProjectList
|
||||
projects={projects}
|
||||
onProjectClick={handleProjectClick}
|
||||
onProjectSettings={(project) => {
|
||||
// Project settings functionality can be added here if needed
|
||||
console.log('Project settings clicked for:', project);
|
||||
}}
|
||||
onNewSession={handleNewSession}
|
||||
loading={loading}
|
||||
className="animate-fade-in"
|
||||
/>
|
||||
@@ -215,6 +198,7 @@ const TabPanel: React.FC<TabPanelProps> = ({ tab, isActive }) => {
|
||||
<ClaudeCodeSession
|
||||
session={tab.sessionData} // Pass the full session object if available
|
||||
initialProjectPath={tab.initialProjectPath || tab.sessionId}
|
||||
tabId={tab.id} // Pass tabId for state synchronization
|
||||
onBack={() => {
|
||||
// Go back to projects view in the same tab
|
||||
updateTab(tab.id, {
|
||||
@@ -294,24 +278,24 @@ const TabPanel: React.FC<TabPanelProps> = ({ tab, isActive }) => {
|
||||
}
|
||||
};
|
||||
|
||||
// Only render content when the tab is active or was previously active (to keep state)
|
||||
// This prevents unnecessary unmounting/remounting
|
||||
const shouldRenderContent = isActive;
|
||||
|
||||
return (
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 10 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
exit={{ opacity: 0, y: -10 }}
|
||||
transition={{ duration: 0.2 }}
|
||||
className={`h-full w-full ${panelVisibilityClass}`}
|
||||
>
|
||||
<Suspense
|
||||
fallback={
|
||||
<div className="flex items-center justify-center h-full">
|
||||
<Loader2 className="w-8 h-8 animate-spin text-muted-foreground" />
|
||||
</div>
|
||||
}
|
||||
>
|
||||
{renderContent()}
|
||||
</Suspense>
|
||||
</motion.div>
|
||||
<div className={`h-full w-full ${panelVisibilityClass}`}>
|
||||
{shouldRenderContent && (
|
||||
<Suspense
|
||||
fallback={
|
||||
<div className="flex items-center justify-center h-full">
|
||||
<Loader2 className="w-8 h-8 animate-spin text-muted-foreground" />
|
||||
</div>
|
||||
}
|
||||
>
|
||||
{renderContent()}
|
||||
</Suspense>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -319,7 +303,7 @@ export const TabContent: React.FC = () => {
|
||||
const { t } = useTranslation();
|
||||
const { tabs, activeTabId, createChatTab, findTabBySessionId, createClaudeFileTab, createAgentExecutionTab, createCreateAgentTab, createImportAgentTab, closeTab, updateTab } = useTabState();
|
||||
const [hasInitialized, setHasInitialized] = React.useState(false);
|
||||
|
||||
|
||||
// Debug: Monitor activeTabId changes
|
||||
useEffect(() => {
|
||||
}, [activeTabId, tabs]);
|
||||
@@ -428,15 +412,13 @@ export const TabContent: React.FC = () => {
|
||||
|
||||
return (
|
||||
<div className="flex-1 h-full relative">
|
||||
<AnimatePresence mode="wait">
|
||||
{tabs.map((tab) => (
|
||||
<TabPanel
|
||||
key={tab.id}
|
||||
tab={tab}
|
||||
isActive={tab.id === activeTabId}
|
||||
/>
|
||||
))}
|
||||
</AnimatePresence>
|
||||
{tabs.map((tab) => (
|
||||
<TabPanel
|
||||
key={tab.id}
|
||||
tab={tab}
|
||||
isActive={tab.id === activeTabId}
|
||||
/>
|
||||
))}
|
||||
|
||||
{tabs.length === 0 && (
|
||||
<div className="flex items-center justify-center h-full text-muted-foreground">
|
||||
|
||||
@@ -176,6 +176,9 @@ export const TabManager: React.FC<TabManagerProps> = ({ className }) => {
|
||||
// Listen for keyboard shortcut events
|
||||
useEffect(() => {
|
||||
const handleCreateTab = () => {
|
||||
if (!canAddTab()) {
|
||||
return;
|
||||
}
|
||||
createChatTab();
|
||||
trackEvent.tabCreated('chat');
|
||||
};
|
||||
@@ -246,7 +249,7 @@ export const TabManager: React.FC<TabManagerProps> = ({ className }) => {
|
||||
window.removeEventListener('switch-to-tab-by-index', handleTabByIndex as EventListener);
|
||||
window.removeEventListener('open-session-tab', handleOpenSessionTab as EventListener);
|
||||
};
|
||||
}, [tabs, activeTabId, createChatTab, closeTab, switchToTab, updateTab, canAddTab]);
|
||||
}, [tabs, activeTabId, createChatTab, closeTab, switchToTab, updateTab, canAddTab, trackEvent]);
|
||||
|
||||
// Check scroll buttons visibility
|
||||
const checkScrollButtons = () => {
|
||||
@@ -427,4 +430,4 @@ export const TabManager: React.FC<TabManagerProps> = ({ className }) => {
|
||||
);
|
||||
};
|
||||
|
||||
export default TabManager;
|
||||
export default TabManager;
|
||||
|
||||
@@ -277,8 +277,17 @@ export const Terminal: React.FC<TerminalProps> = ({
|
||||
resizeTerminal();
|
||||
}, 150);
|
||||
|
||||
// 如果没有有效的 projectPath,跳过创建终端会话
|
||||
if (!projectPath || projectPath.trim() === '') {
|
||||
console.log('[Terminal] Skipping session creation - no project path');
|
||||
if (xtermRef.current) {
|
||||
xtermRef.current.write('\r\n\x1b[33mNo project directory selected. Please select a project to use the terminal.\x1b[0m\r\n');
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// 创建终端会话
|
||||
const newSessionId = await api.createTerminalSession(projectPath || process.cwd());
|
||||
const newSessionId = await api.createTerminalSession(projectPath);
|
||||
|
||||
if (!isMounted) {
|
||||
await api.closeTerminalSession(newSessionId);
|
||||
|
||||
Reference in New Issue
Block a user