修复错误bug
This commit is contained in:
@@ -439,17 +439,6 @@ export const ClaudeCodeSession: React.FC<ClaudeCodeSessionProps> = ({
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Debug logging
|
// Debug logging
|
||||||
useEffect(() => {
|
|
||||||
console.log('[ClaudeCodeSession] State update:', {
|
|
||||||
projectPath,
|
|
||||||
session,
|
|
||||||
extractedSessionInfo,
|
|
||||||
effectiveSession,
|
|
||||||
messagesCount: messages.length,
|
|
||||||
isLoading
|
|
||||||
});
|
|
||||||
}, [projectPath, session, extractedSessionInfo, effectiveSession, messages.length, isLoading]);
|
|
||||||
|
|
||||||
// Load session history if resuming
|
// Load session history if resuming
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (session) {
|
if (session) {
|
||||||
@@ -479,7 +468,6 @@ export const ClaudeCodeSession: React.FC<ClaudeCodeSessionProps> = ({
|
|||||||
if (tabId && projectPath && !session) {
|
if (tabId && projectPath && !session) {
|
||||||
// Only update for new sessions (not resumed sessions)
|
// Only update for new sessions (not resumed sessions)
|
||||||
// This ensures the path is saved when user selects/enters it
|
// This ensures the path is saved when user selects/enters it
|
||||||
console.log('[ClaudeCodeSession] Syncing projectPath to tab:', { tabId, projectPath });
|
|
||||||
updateTab(tabId, {
|
updateTab(tabId, {
|
||||||
initialProjectPath: projectPath
|
initialProjectPath: projectPath
|
||||||
});
|
});
|
||||||
@@ -571,7 +559,6 @@ export const ClaudeCodeSession: React.FC<ClaudeCodeSessionProps> = ({
|
|||||||
|
|
||||||
if (activeSession) {
|
if (activeSession) {
|
||||||
// Session is still active, reconnect to its stream
|
// Session is still active, reconnect to its stream
|
||||||
console.log('[ClaudeCodeSession] Found active session, reconnecting:', session.id);
|
|
||||||
// IMPORTANT: Set claudeSessionId before reconnecting
|
// IMPORTANT: Set claudeSessionId before reconnecting
|
||||||
setClaudeSessionId(session.id);
|
setClaudeSessionId(session.id);
|
||||||
|
|
||||||
@@ -588,11 +575,9 @@ export const ClaudeCodeSession: React.FC<ClaudeCodeSessionProps> = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const reconnectToSession = async (sessionId: string) => {
|
const reconnectToSession = async (sessionId: string) => {
|
||||||
console.log('[ClaudeCodeSession] Reconnecting to session:', sessionId);
|
|
||||||
|
|
||||||
// Prevent duplicate listeners
|
// Prevent duplicate listeners
|
||||||
if (isListeningRef.current) {
|
if (isListeningRef.current) {
|
||||||
console.log('[ClaudeCodeSession] Already listening to session, skipping reconnect');
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -609,7 +594,6 @@ export const ClaudeCodeSession: React.FC<ClaudeCodeSessionProps> = ({
|
|||||||
// Set up session-specific listeners
|
// Set up session-specific listeners
|
||||||
const outputUnlisten = await listen<string>(`claude-output:${sessionId}`, async (event) => {
|
const outputUnlisten = await listen<string>(`claude-output:${sessionId}`, async (event) => {
|
||||||
try {
|
try {
|
||||||
console.log('[ClaudeCodeSession] Received claude-output on reconnect:', event.payload);
|
|
||||||
|
|
||||||
if (!isMountedRef.current) return;
|
if (!isMountedRef.current) return;
|
||||||
|
|
||||||
@@ -632,7 +616,6 @@ export const ClaudeCodeSession: React.FC<ClaudeCodeSessionProps> = ({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const completeUnlisten = await listen<boolean>(`claude-complete:${sessionId}`, async (event) => {
|
const completeUnlisten = await listen<boolean>(`claude-complete:${sessionId}`, async (event) => {
|
||||||
console.log('[ClaudeCodeSession] Received claude-complete on reconnect:', event.payload);
|
|
||||||
if (isMountedRef.current) {
|
if (isMountedRef.current) {
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
hasActiveSessionRef.current = false;
|
hasActiveSessionRef.current = false;
|
||||||
@@ -668,7 +651,6 @@ export const ClaudeCodeSession: React.FC<ClaudeCodeSessionProps> = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleSendPrompt = async (prompt: string, model: "sonnet" | "opus" | "opus-plan") => {
|
const handleSendPrompt = async (prompt: string, model: "sonnet" | "opus" | "opus-plan") => {
|
||||||
console.log('[ClaudeCodeSession] handleSendPrompt called with:', { prompt, model, projectPath, claudeSessionId, effectiveSession });
|
|
||||||
|
|
||||||
if (!projectPath) {
|
if (!projectPath) {
|
||||||
setError(t('app.selectProjectFirst'));
|
setError(t('app.selectProjectFirst'));
|
||||||
@@ -718,13 +700,11 @@ export const ClaudeCodeSession: React.FC<ClaudeCodeSessionProps> = ({
|
|||||||
// generic ones to prevent duplicate handling.
|
// generic ones to prevent duplicate handling.
|
||||||
// --------------------------------------------------------------------
|
// --------------------------------------------------------------------
|
||||||
|
|
||||||
console.log('[ClaudeCodeSession] Setting up generic event listeners first');
|
|
||||||
|
|
||||||
let currentSessionId: string | null = claudeSessionId || effectiveSession?.id || null;
|
let currentSessionId: string | null = claudeSessionId || effectiveSession?.id || null;
|
||||||
|
|
||||||
// Helper to attach session-specific listeners **once we are sure**
|
// Helper to attach session-specific listeners **once we are sure**
|
||||||
const attachSessionSpecificListeners = async (sid: string) => {
|
const attachSessionSpecificListeners = async (sid: string) => {
|
||||||
console.log('[ClaudeCodeSession] Attaching session-specific listeners for', sid);
|
|
||||||
|
|
||||||
const specificOutputUnlisten = await listen<string>(`claude-output:${sid}`, (evt) => {
|
const specificOutputUnlisten = await listen<string>(`claude-output:${sid}`, (evt) => {
|
||||||
handleStreamMessage(evt.payload);
|
handleStreamMessage(evt.payload);
|
||||||
@@ -736,7 +716,6 @@ export const ClaudeCodeSession: React.FC<ClaudeCodeSessionProps> = ({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const specificCompleteUnlisten = await listen<boolean>(`claude-complete:${sid}`, (evt) => {
|
const specificCompleteUnlisten = await listen<boolean>(`claude-complete:${sid}`, (evt) => {
|
||||||
console.log('[ClaudeCodeSession] Received claude-complete (scoped):', evt.payload);
|
|
||||||
processComplete(evt.payload);
|
processComplete(evt.payload);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -754,7 +733,6 @@ export const ClaudeCodeSession: React.FC<ClaudeCodeSessionProps> = ({
|
|||||||
const msg = JSON.parse(event.payload) as ClaudeStreamMessage;
|
const msg = JSON.parse(event.payload) as ClaudeStreamMessage;
|
||||||
if (msg.type === 'system' && msg.subtype === 'init' && msg.session_id) {
|
if (msg.type === 'system' && msg.subtype === 'init' && msg.session_id) {
|
||||||
if (!currentSessionId || currentSessionId !== msg.session_id) {
|
if (!currentSessionId || currentSessionId !== msg.session_id) {
|
||||||
console.log('[ClaudeCodeSession] Detected new session_id from generic listener:', msg.session_id);
|
|
||||||
currentSessionId = msg.session_id;
|
currentSessionId = msg.session_id;
|
||||||
setClaudeSessionId(msg.session_id);
|
setClaudeSessionId(msg.session_id);
|
||||||
|
|
||||||
@@ -962,7 +940,6 @@ export const ClaudeCodeSession: React.FC<ClaudeCodeSessionProps> = ({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const genericCompleteUnlisten = await listen<boolean>('claude-complete', (evt) => {
|
const genericCompleteUnlisten = await listen<boolean>('claude-complete', (evt) => {
|
||||||
console.log('[ClaudeCodeSession] Received claude-complete (generic):', evt.payload);
|
|
||||||
processComplete(evt.payload);
|
processComplete(evt.payload);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -1029,12 +1006,10 @@ export const ClaudeCodeSession: React.FC<ClaudeCodeSessionProps> = ({
|
|||||||
|
|
||||||
// Execute the appropriate command
|
// Execute the appropriate command
|
||||||
if (effectiveSession && !isFirstPrompt) {
|
if (effectiveSession && !isFirstPrompt) {
|
||||||
console.log('[ClaudeCodeSession] Resuming session:', effectiveSession.id);
|
|
||||||
trackEvent.sessionResumed(effectiveSession.id);
|
trackEvent.sessionResumed(effectiveSession.id);
|
||||||
trackEvent.modelSelected(model);
|
trackEvent.modelSelected(model);
|
||||||
await api.resumeClaudeCode(projectPath, effectiveSession.id, prompt, model);
|
await api.resumeClaudeCode(projectPath, effectiveSession.id, prompt, model);
|
||||||
} else {
|
} else {
|
||||||
console.log('[ClaudeCodeSession] Starting new session');
|
|
||||||
setIsFirstPrompt(false);
|
setIsFirstPrompt(false);
|
||||||
trackEvent.sessionCreated(model, 'prompt_input');
|
trackEvent.sessionCreated(model, 'prompt_input');
|
||||||
trackEvent.modelSelected(model);
|
trackEvent.modelSelected(model);
|
||||||
@@ -1329,7 +1304,6 @@ export const ClaudeCodeSession: React.FC<ClaudeCodeSessionProps> = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handlePreviewUrlChange = (url: string) => {
|
const handlePreviewUrlChange = (url: string) => {
|
||||||
console.log('[ClaudeCodeSession] Preview URL changed to:', url);
|
|
||||||
openLayoutPreview(url);
|
openLayoutPreview(url);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1338,7 +1312,6 @@ export const ClaudeCodeSession: React.FC<ClaudeCodeSessionProps> = ({
|
|||||||
isMountedRef.current = true;
|
isMountedRef.current = true;
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
console.log('[ClaudeCodeSession] Component unmounting, cleaning up listeners');
|
|
||||||
isMountedRef.current = false;
|
isMountedRef.current = false;
|
||||||
isListeningRef.current = false;
|
isListeningRef.current = false;
|
||||||
|
|
||||||
|
|||||||
@@ -245,8 +245,6 @@ const FloatingPromptInputInner = (
|
|||||||
|
|
||||||
// Extract image paths from prompt text
|
// Extract image paths from prompt text
|
||||||
const extractImagePaths = (text: string): string[] => {
|
const extractImagePaths = (text: string): string[] => {
|
||||||
console.log('[extractImagePaths] Input text length:', text.length);
|
|
||||||
|
|
||||||
// Updated regex to handle both quoted and unquoted paths
|
// Updated regex to handle both quoted and unquoted paths
|
||||||
// Pattern 1: @"path with spaces or data URLs" - quoted paths
|
// Pattern 1: @"path with spaces or data URLs" - quoted paths
|
||||||
// Pattern 2: @path - unquoted paths (continues until @ or end)
|
// Pattern 2: @path - unquoted paths (continues until @ or end)
|
||||||
@@ -257,11 +255,9 @@ const FloatingPromptInputInner = (
|
|||||||
|
|
||||||
// First, extract quoted paths (including data URLs)
|
// First, extract quoted paths (including data URLs)
|
||||||
let matches = Array.from(text.matchAll(quotedRegex));
|
let matches = Array.from(text.matchAll(quotedRegex));
|
||||||
console.log('[extractImagePaths] Quoted matches:', matches.length);
|
|
||||||
|
|
||||||
for (const match of matches) {
|
for (const match of matches) {
|
||||||
const path = match[1]; // No need to trim, quotes preserve exact path
|
const path = match[1]; // No need to trim, quotes preserve exact path
|
||||||
console.log('[extractImagePaths] Processing quoted path:', path.startsWith('data:') ? 'data URL' : path);
|
|
||||||
|
|
||||||
// For data URLs, use as-is; for file paths, convert to absolute
|
// For data URLs, use as-is; for file paths, convert to absolute
|
||||||
const fullPath = path.startsWith('data:')
|
const fullPath = path.startsWith('data:')
|
||||||
@@ -278,15 +274,12 @@ const FloatingPromptInputInner = (
|
|||||||
|
|
||||||
// Then extract unquoted paths (typically file paths)
|
// Then extract unquoted paths (typically file paths)
|
||||||
matches = Array.from(textWithoutQuoted.matchAll(unquotedRegex));
|
matches = Array.from(textWithoutQuoted.matchAll(unquotedRegex));
|
||||||
console.log('[extractImagePaths] Unquoted matches:', matches.length);
|
|
||||||
|
|
||||||
for (const match of matches) {
|
for (const match of matches) {
|
||||||
const path = match[1].trim();
|
const path = match[1].trim();
|
||||||
// Skip if it looks like a data URL fragment (shouldn't happen with proper quoting)
|
// Skip if it looks like a data URL fragment (shouldn't happen with proper quoting)
|
||||||
if (path.includes('data:')) continue;
|
if (path.includes('data:')) continue;
|
||||||
|
|
||||||
console.log('[extractImagePaths] Processing unquoted path:', path);
|
|
||||||
|
|
||||||
// Convert relative path to absolute if needed
|
// Convert relative path to absolute if needed
|
||||||
const fullPath = path.startsWith('/') ? path : (projectPath ? `${projectPath}/${path}` : path);
|
const fullPath = path.startsWith('/') ? path : (projectPath ? `${projectPath}/${path}` : path);
|
||||||
|
|
||||||
@@ -295,16 +288,12 @@ const FloatingPromptInputInner = (
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const uniquePaths = Array.from(pathsSet);
|
return Array.from(pathsSet);
|
||||||
console.log('[extractImagePaths] Final extracted paths (unique):', uniquePaths.length);
|
|
||||||
return uniquePaths;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Update embedded images when prompt changes
|
// Update embedded images when prompt changes
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
console.log('[useEffect] Prompt changed:', prompt);
|
|
||||||
const imagePaths = extractImagePaths(prompt);
|
const imagePaths = extractImagePaths(prompt);
|
||||||
console.log('[useEffect] Setting embeddedImages to:', imagePaths);
|
|
||||||
setEmbeddedImages(imagePaths);
|
setEmbeddedImages(imagePaths);
|
||||||
}, [prompt, projectPath]);
|
}, [prompt, projectPath]);
|
||||||
|
|
||||||
@@ -422,7 +411,6 @@ const FloatingPromptInputInner = (
|
|||||||
(newCursorPosition > 1 && /\s/.test(newValue[newCursorPosition - 2]));
|
(newCursorPosition > 1 && /\s/.test(newValue[newCursorPosition - 2]));
|
||||||
|
|
||||||
if (isStartOfCommand) {
|
if (isStartOfCommand) {
|
||||||
console.log('[FloatingPromptInput] / detected for slash command');
|
|
||||||
setShowSlashCommandPicker(true);
|
setShowSlashCommandPicker(true);
|
||||||
setSlashCommandQuery("");
|
setSlashCommandQuery("");
|
||||||
setCursorPosition(newCursorPosition);
|
setCursorPosition(newCursorPosition);
|
||||||
@@ -431,7 +419,6 @@ const FloatingPromptInputInner = (
|
|||||||
|
|
||||||
// Check if @ was just typed
|
// Check if @ was just typed
|
||||||
if (projectPath?.trim() && newValue.length > prompt.length && newValue[newCursorPosition - 1] === '@') {
|
if (projectPath?.trim() && newValue.length > prompt.length && newValue[newCursorPosition - 1] === '@') {
|
||||||
console.log('[FloatingPromptInput] @ detected, projectPath:', projectPath);
|
|
||||||
setShowFilePicker(true);
|
setShowFilePicker(true);
|
||||||
setFilePickerQuery("");
|
setFilePickerQuery("");
|
||||||
setCursorPosition(newCursorPosition);
|
setCursorPosition(newCursorPosition);
|
||||||
|
|||||||
@@ -54,21 +54,6 @@ export const Terminal: React.FC<TerminalProps> = ({
|
|||||||
const rows = Math.max(24, Math.floor(availableHeight / lineHeight));
|
const rows = Math.max(24, Math.floor(availableHeight / lineHeight));
|
||||||
|
|
||||||
// 计算实际使用的宽度
|
// 计算实际使用的宽度
|
||||||
const usedWidth = cols * charWidth;
|
|
||||||
const unusedWidth = availableWidth - usedWidth;
|
|
||||||
const percentUsed = ((usedWidth / availableWidth) * 100).toFixed(1);
|
|
||||||
|
|
||||||
console.log('[Terminal] Size calculation:', {
|
|
||||||
containerWidth: rect.width,
|
|
||||||
availableWidth,
|
|
||||||
charWidth,
|
|
||||||
cols,
|
|
||||||
usedWidth,
|
|
||||||
unusedWidth,
|
|
||||||
percentUsed: `${percentUsed}%`,
|
|
||||||
message: unusedWidth > 10 ? `还有 ${unusedWidth.toFixed(1)}px 未使用` : '宽度使用正常'
|
|
||||||
});
|
|
||||||
|
|
||||||
return { cols, rows };
|
return { cols, rows };
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
@@ -87,10 +72,6 @@ export const Terminal: React.FC<TerminalProps> = ({
|
|||||||
if (dims.actualCellWidth) actualCharWidth = dims.actualCellWidth;
|
if (dims.actualCellWidth) actualCharWidth = dims.actualCellWidth;
|
||||||
if (dims.actualCellHeight) actualLineHeight = dims.actualCellHeight;
|
if (dims.actualCellHeight) actualLineHeight = dims.actualCellHeight;
|
||||||
|
|
||||||
console.log('[Terminal] Using actual char dimensions:', {
|
|
||||||
actualCharWidth,
|
|
||||||
actualLineHeight
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// 使用默认值
|
// 使用默认值
|
||||||
@@ -108,22 +89,8 @@ export const Terminal: React.FC<TerminalProps> = ({
|
|||||||
const newRows = Math.max(24, Math.floor(availableHeight / actualLineHeight));
|
const newRows = Math.max(24, Math.floor(availableHeight / actualLineHeight));
|
||||||
|
|
||||||
// 计算宽度使用情况
|
// 计算宽度使用情况
|
||||||
const usedWidth = newCols * actualCharWidth;
|
|
||||||
const unusedWidth = availableWidth - usedWidth;
|
|
||||||
const percentUsed = ((usedWidth / availableWidth) * 100).toFixed(1);
|
|
||||||
|
|
||||||
// 只有当尺寸真的改变时才调整
|
// 只有当尺寸真的改变时才调整
|
||||||
if (newCols !== terminalSize.cols || newRows !== terminalSize.rows) {
|
if (newCols !== terminalSize.cols || newRows !== terminalSize.rows) {
|
||||||
console.log('[Terminal] Resizing:', {
|
|
||||||
from: terminalSize,
|
|
||||||
to: { cols: newCols, rows: newRows },
|
|
||||||
containerWidth: rect.width,
|
|
||||||
availableWidth,
|
|
||||||
usedWidth,
|
|
||||||
unusedWidth,
|
|
||||||
percentUsed: `${percentUsed}%`
|
|
||||||
});
|
|
||||||
|
|
||||||
setTerminalSize({ cols: newCols, rows: newRows });
|
setTerminalSize({ cols: newCols, rows: newRows });
|
||||||
xtermRef.current.resize(newCols, newRows);
|
xtermRef.current.resize(newCols, newRows);
|
||||||
|
|
||||||
@@ -163,7 +130,6 @@ export const Terminal: React.FC<TerminalProps> = ({
|
|||||||
|
|
||||||
const initializeTerminal = async () => {
|
const initializeTerminal = async () => {
|
||||||
try {
|
try {
|
||||||
console.log('[Terminal] Initializing...');
|
|
||||||
isInitializedRef.current = true;
|
isInitializedRef.current = true;
|
||||||
|
|
||||||
// 先计算初始尺寸
|
// 先计算初始尺寸
|
||||||
@@ -247,23 +213,11 @@ export const Terminal: React.FC<TerminalProps> = ({
|
|||||||
const actualLineHeight = dims.actualCellHeight || dims.scaledCellHeight;
|
const actualLineHeight = dims.actualCellHeight || dims.scaledCellHeight;
|
||||||
|
|
||||||
if (actualCharWidth && actualLineHeight) {
|
if (actualCharWidth && actualLineHeight) {
|
||||||
console.log('[Terminal] Actual character dimensions:', {
|
|
||||||
charWidth: actualCharWidth,
|
|
||||||
lineHeight: actualLineHeight
|
|
||||||
});
|
|
||||||
|
|
||||||
// 使用实际尺寸重新计算
|
// 使用实际尺寸重新计算
|
||||||
const rect = terminalRef.current.getBoundingClientRect();
|
const rect = terminalRef.current.getBoundingClientRect();
|
||||||
const availableWidth = rect.width - 2;
|
const availableWidth = rect.width - 2;
|
||||||
const newCols = Math.floor(availableWidth / actualCharWidth);
|
const newCols = Math.floor(availableWidth / actualCharWidth);
|
||||||
|
|
||||||
console.log('[Terminal] Recalculating with actual dimensions:', {
|
|
||||||
availableWidth,
|
|
||||||
actualCharWidth,
|
|
||||||
newCols,
|
|
||||||
currentCols: xtermRef.current.cols
|
|
||||||
});
|
|
||||||
|
|
||||||
if (newCols > xtermRef.current.cols) {
|
if (newCols > xtermRef.current.cols) {
|
||||||
xtermRef.current.resize(newCols, xtermRef.current.rows);
|
xtermRef.current.resize(newCols, xtermRef.current.rows);
|
||||||
setTerminalSize({ cols: newCols, rows: xtermRef.current.rows });
|
setTerminalSize({ cols: newCols, rows: xtermRef.current.rows });
|
||||||
@@ -279,7 +233,6 @@ export const Terminal: React.FC<TerminalProps> = ({
|
|||||||
|
|
||||||
// 如果没有有效的 projectPath,跳过创建终端会话
|
// 如果没有有效的 projectPath,跳过创建终端会话
|
||||||
if (!projectPath || projectPath.trim() === '') {
|
if (!projectPath || projectPath.trim() === '') {
|
||||||
console.log('[Terminal] Skipping session creation - no project path');
|
|
||||||
if (xtermRef.current) {
|
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');
|
xtermRef.current.write('\r\n\x1b[33mNo project directory selected. Please select a project to use the terminal.\x1b[0m\r\n');
|
||||||
}
|
}
|
||||||
@@ -315,8 +268,6 @@ export const Terminal: React.FC<TerminalProps> = ({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log('[Terminal] Initialized with session:', newSessionId);
|
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('[Terminal] Failed to initialize:', error);
|
console.error('[Terminal] Failed to initialize:', error);
|
||||||
if (xtermRef.current && isMounted) {
|
if (xtermRef.current && isMounted) {
|
||||||
|
|||||||
Reference in New Issue
Block a user