From e7775ce8ed4d069ae256821658250f2f1666412e Mon Sep 17 00:00:00 2001 From: YoVinchen Date: Sat, 11 Oct 2025 13:00:52 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8Dagents=E5=8E=86=E5=8F=B2?= =?UTF-8?q?=E8=AE=B0=E5=BD=95=E8=B7=B3=E8=BD=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/App.tsx | 22 ++++++++++++++++++- src/components/AgentRunOutputViewer.tsx | 28 +++---------------------- src/components/AgentRunsList.tsx | 13 +++++++++--- src/components/TabContent.tsx | 7 +++++++ src/components/WebviewPreview.tsx | 6 +----- src/contexts/TabContext.tsx | 7 +++++-- src/hooks/useTabState.ts | 5 +++-- src/lib/i18n.ts | 5 +++-- 8 files changed, 53 insertions(+), 40 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index 76f93e5..0195b3a 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -90,7 +90,6 @@ function AppContent() { const backendLocale = frontendLang === 'zh' ? 'zh-CN' : 'en-US'; // Sync to backend await api.setLanguage(backendLocale); - console.log('Backend language initialized to:', backendLocale); } catch (error) { console.error('Failed to initialize backend language:', error); } @@ -187,6 +186,27 @@ function AppContent() { }; }, []); + // Listen for a global request to switch to the tabbed interface + useEffect(() => { + const handleSwitchToTabs = (event: Event) => { + // Accept optional tabId in event detail + const detail = (event as CustomEvent).detail || {}; + const tabId = detail.tabId as string | undefined; + setView('tabs'); + if (tabId) { + // Wait a tick for TabManager to mount, then switch to the tab + setTimeout(() => { + window.dispatchEvent(new CustomEvent('switch-to-tab', { detail: { tabId } })); + }, 50); + } + }; + + window.addEventListener('switch-to-tabs', handleSwitchToTabs as EventListener); + return () => { + window.removeEventListener('switch-to-tabs', handleSwitchToTabs as EventListener); + }; + }, []); + /** * Loads all projects from the ~/.claude/projects directory */ diff --git a/src/components/AgentRunOutputViewer.tsx b/src/components/AgentRunOutputViewer.tsx index ad1339a..64189f0 100644 --- a/src/components/AgentRunOutputViewer.tsx +++ b/src/components/AgentRunOutputViewer.tsx @@ -58,6 +58,7 @@ export function AgentRunOutputViewer({ tabId, className }: AgentRunOutputViewerProps) { + const { t } = useTranslation(); const { updateTabTitle, updateTabStatus } = useTabState(); const [run, setRun] = useState(null); @@ -143,25 +144,17 @@ export function AgentRunOutputViewer({ const loadOutput = async (skipCache = false) => { if (!run?.id) return; - console.log('[AgentRunOutputViewer] Loading output for run:', { - runId: run.id, - status: run.status, - sessionId: run.session_id, - skipCache - }); try { // Check cache first if not skipping cache if (!skipCache) { const cached = getCachedOutput(run.id); if (cached) { - console.log('[AgentRunOutputViewer] Found cached output'); const cachedJsonlLines = cached.output.split('\n').filter(line => line.trim()); setRawJsonlOutput(cachedJsonlLines); setMessages(cached.messages); // If cache is recent (less than 5 seconds old) and session isn't running, use cache only if (Date.now() - cached.lastUpdated < 5000 && run.status !== 'running') { - console.log('[AgentRunOutputViewer] Using recent cache, skipping refresh'); return; } } @@ -171,10 +164,8 @@ export function AgentRunOutputViewer({ // If we have a session_id, try to load from JSONL file first if (run.session_id && run.session_id !== '') { - console.log('[AgentRunOutputViewer] Attempting to load from JSONL with session_id:', run.session_id); try { const history = await api.loadAgentSessionHistory(run.session_id); - console.log('[AgentRunOutputViewer] Successfully loaded JSONL history:', history.length, 'messages'); // Convert history to messages format const loadedMessages: ClaudeStreamMessage[] = history.map(entry => ({ @@ -195,29 +186,22 @@ export function AgentRunOutputViewer({ // Set up live event listeners for running sessions if (run.status === 'running') { - console.log('[AgentRunOutputViewer] Setting up live listeners for running session'); setupLiveEventListeners(); try { await api.streamSessionOutput(run.id); } catch (streamError) { - console.warn('[AgentRunOutputViewer] Failed to start streaming, will poll instead:', streamError); } } return; } catch (err) { - console.warn('[AgentRunOutputViewer] Failed to load from JSONL:', err); - console.warn('[AgentRunOutputViewer] Falling back to regular output method'); + // Fallback path will be used if JSONL loading fails } } else { - console.log('[AgentRunOutputViewer] No session_id available, using fallback method'); } - // Fallback to the original method if JSONL loading fails or no session_id - console.log('[AgentRunOutputViewer] Using getSessionOutput fallback'); const rawOutput = await api.getSessionOutput(run.id); - console.log('[AgentRunOutputViewer] Received raw output:', rawOutput.length, 'characters'); // Parse JSONL output into messages const jsonlLines = rawOutput.split('\n').filter(line => line.trim()); @@ -232,7 +216,6 @@ export function AgentRunOutputViewer({ console.error("[AgentRunOutputViewer] Failed to parse message:", err, line); } } - console.log('[AgentRunOutputViewer] Parsed', parsedMessages.length, 'messages from output'); setMessages(parsedMessages); // Update cache @@ -245,13 +228,11 @@ export function AgentRunOutputViewer({ // Set up live event listeners for running sessions if (run.status === 'running') { - console.log('[AgentRunOutputViewer] Setting up live listeners for running session (fallback)'); setupLiveEventListeners(); try { await api.streamSessionOutput(run.id); } catch (streamError) { - console.warn('[AgentRunOutputViewer] Failed to start streaming (fallback), will poll instead:', streamError); } } } catch (error) { @@ -285,7 +266,6 @@ export function AgentRunOutputViewer({ try { // Skip messages during initial load phase if (isInitialLoadRef.current) { - console.log('[AgentRunOutputViewer] Skipping message during initial load'); return; } @@ -403,7 +383,6 @@ export function AgentRunOutputViewer({ const success = await api.killAgentSession(run.id); if (success) { - console.log(`[AgentRunOutputViewer] Successfully stopped agent session ${run.id}`); setToast({ message: 'Agent execution stopped', type: 'success' }); // Clean up listeners @@ -431,7 +410,6 @@ export function AgentRunOutputViewer({ // Refresh the output to get updated status await loadOutput(true); } else { - console.warn(`[AgentRunOutputViewer] Failed to stop agent session ${run.id} - it may have already finished`); setToast({ message: 'Failed to stop agent - it may have already finished', type: 'error' }); } } catch (err) { @@ -828,4 +806,4 @@ export function AgentRunOutputViewer({ ); } -export default AgentRunOutputViewer; \ No newline at end of file +export default AgentRunOutputViewer; diff --git a/src/components/AgentRunsList.tsx b/src/components/AgentRunsList.tsx index d72ec8b..6b38660 100644 --- a/src/components/AgentRunsList.tsx +++ b/src/components/AgentRunsList.tsx @@ -98,12 +98,19 @@ export const AgentRunsList: React.FC = ({ }; const handleRunClick = (run: AgentRunWithMetrics) => { + + if (!run.id) { + console.error('[AgentRunsList] Cannot open run - no ID available:', run); + return; + } + // If there's a callback, use it (for full-page navigation) if (onRunClick) { onRunClick(run); - } else if (run.id) { + } else { // Otherwise, open in new tab - createAgentTab(run.id.toString(), run.agent_name); + const tabId = createAgentTab(run.id.toString(), run.agent_name); + window.dispatchEvent(new CustomEvent('switch-to-tabs', { detail: { tabId } })); } }; @@ -215,4 +222,4 @@ export const AgentRunsList: React.FC = ({ ); -}; \ No newline at end of file +}; diff --git a/src/components/TabContent.tsx b/src/components/TabContent.tsx index 5b488bd..873007c 100644 --- a/src/components/TabContent.tsx +++ b/src/components/TabContent.tsx @@ -226,9 +226,12 @@ const TabPanel: React.FC = ({ tab, isActive }) => { ); case 'agent': + if (!tab.agentRunId) { + console.error('[TabContent] No agentRunId in tab:', tab); return
{t('messages.noAgentRunIdSpecified')}
; } + return ( { 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]); + // Auto redirect to home when no tabs (but not on initial load) useEffect(() => { if (hasInitialized && tabs.length === 0) { diff --git a/src/components/WebviewPreview.tsx b/src/components/WebviewPreview.tsx index afb71a3..dc32f8d 100644 --- a/src/components/WebviewPreview.tsx +++ b/src/components/WebviewPreview.tsx @@ -93,7 +93,6 @@ const WebviewPreviewComponent: React.FC = ({ // Debug: Log initial URL on mount useEffect(() => { - console.log('[WebviewPreview] Component mounted with initialUrl:', initialUrl, 'isMaximized:', isMaximized); }, []); // Focus management for full screen mode @@ -127,7 +126,6 @@ const WebviewPreviewComponent: React.FC = ({ const urlObj = new URL(url.startsWith('http') ? url : `https://${url}`); const finalUrl = urlObj.href; - console.log('[WebviewPreview] Navigating to:', finalUrl); setCurrentUrl(finalUrl); setInputUrl(finalUrl); setHasError(false); @@ -152,12 +150,10 @@ const WebviewPreviewComponent: React.FC = ({ const handleGoBack = () => { // In real implementation, this would call webview.goBack() - console.log("Go back"); }; const handleGoForward = () => { // In real implementation, this would call webview.goForward() - console.log("Go forward"); }; const handleRefresh = () => { @@ -354,4 +350,4 @@ const WebviewPreviewComponent: React.FC = ({ ); }; -export const WebviewPreview = React.memo(WebviewPreviewComponent); \ No newline at end of file +export const WebviewPreview = React.memo(WebviewPreviewComponent); diff --git a/src/contexts/TabContext.tsx b/src/contexts/TabContext.tsx index c00e2eb..86eb1ef 100644 --- a/src/contexts/TabContext.tsx +++ b/src/contexts/TabContext.tsx @@ -42,6 +42,7 @@ export const TabProvider: React.FC<{ children: React.ReactNode }> = ({ children const [tabs, setTabs] = useState([]); const [activeTabId, setActiveTabId] = useState(null); + // Always start with a fresh CC Projects tab useEffect(() => { // Create default projects tab @@ -90,6 +91,7 @@ export const TabProvider: React.FC<{ children: React.ReactNode }> = ({ children setTabs(prevTabs => [...prevTabs, newTab]); setActiveTabId(newTab.id); + return newTab.id; }, [tabs.length, t]); @@ -127,10 +129,11 @@ export const TabProvider: React.FC<{ children: React.ReactNode }> = ({ children }, []); const setActiveTab = useCallback((id: string) => { - if (tabs.find(tab => tab.id === id)) { + const tabExists = tabs.find(tab => tab.id === id); + if (tabExists) { setActiveTabId(id); } - }, [tabs]); + }, [tabs, activeTabId]); const reorderTabs = useCallback((startIndex: number, endIndex: number) => { setTabs(prevTabs => { diff --git a/src/hooks/useTabState.ts b/src/hooks/useTabState.ts index be6de94..b82884b 100644 --- a/src/hooks/useTabState.ts +++ b/src/hooks/useTabState.ts @@ -82,7 +82,7 @@ export const useTabState = (): UseTabStateReturn => { return existingTab.id; } - return addTab({ + const newTabId = addTab({ type: 'agent', title: agentName, agentRunId, @@ -90,6 +90,7 @@ export const useTabState = (): UseTabStateReturn => { hasUnsavedChanges: false, icon: 'bot' }); + return newTabId; }, [addTab, tabs, setActiveTab]); const createProjectsTab = useCallback((): string | null => { @@ -346,4 +347,4 @@ export const useTabState = (): UseTabStateReturn => { findTabByType, canAddTab }; -}; \ No newline at end of file +}; diff --git a/src/lib/i18n.ts b/src/lib/i18n.ts index fa72710..f8c2dc6 100644 --- a/src/lib/i18n.ts +++ b/src/lib/i18n.ts @@ -16,14 +16,15 @@ const languageDetectorOptions = { checkWhitelist: true, }; +const i18nDebug = (typeof import.meta !== 'undefined' && (import.meta as any).env?.VITE_I18N_DEBUG === 'true'); + i18n .use(LanguageDetector) .use(initReactI18next) .init({ // 回退语言 fallbackLng: 'en', - // 调试模式(开发环境) - debug: process.env.NODE_ENV === 'development', + debug: i18nDebug, // 语言资源 resources: {