feat: enhance follow-up feature handling and improve async processing

- Implemented asynchronous follow-up work in AutoModeService to allow immediate API response, enabling smoother user experience.
- Updated BoardView to handle follow-up prompts more efficiently, including state management and success notifications.
- Adjusted mock AutoMode API to simulate background processing for follow-up tasks, aligning with real implementation behavior.

These changes streamline the workflow for sending follow-up prompts and improve the responsiveness of the UI.
This commit is contained in:
Kacper
2025-12-09 22:43:33 +01:00
parent bfc0934ce9
commit 835d1ed021
3 changed files with 57 additions and 44 deletions

View File

@@ -557,6 +557,21 @@ class AutoModeService {
execution.sendToRenderer = sendToRenderer; execution.sendToRenderer = sendToRenderer;
this.runningFeatures.set(featureId, execution); this.runningFeatures.set(featureId, execution);
// Start the async work in the background (don't await)
// This allows the API to return immediately so the modal can close
this.runFollowUpWork({ projectPath, featureId, prompt, imagePaths, sendToRenderer, execution }).catch((error) => {
console.error("[AutoMode] Follow-up work error:", error);
this.runningFeatures.delete(featureId);
});
// Return immediately so the frontend can close the modal
return { success: true };
}
/**
* Internal method to run follow-up work asynchronously
*/
async runFollowUpWork({ projectPath, featureId, prompt, imagePaths, sendToRenderer, execution }) {
try { try {
// Load features // Load features
const features = await featureLoader.loadFeatures(projectPath); const features = await featureLoader.loadFeatures(projectPath);
@@ -611,8 +626,6 @@ class AutoModeService {
passes: result.passes, passes: result.passes,
message: result.message, message: result.message,
}); });
return { success: true, passes: result.passes };
} catch (error) { } catch (error) {
console.error("[AutoMode] Error in follow-up:", error); console.error("[AutoMode] Error in follow-up:", error);
sendToRenderer({ sendToRenderer({
@@ -620,7 +633,6 @@ class AutoModeService {
error: error.message, error: error.message,
featureId: featureId, featureId: featureId,
}); });
throw error;
} finally { } finally {
this.runningFeatures.delete(featureId); this.runningFeatures.delete(featureId);
} }

View File

@@ -706,56 +706,55 @@ export function BoardView() {
const handleSendFollowUp = async () => { const handleSendFollowUp = async () => {
if (!currentProject || !followUpFeature || !followUpPrompt.trim()) return; if (!currentProject || !followUpFeature || !followUpPrompt.trim()) return;
// Save values before clearing state
const featureId = followUpFeature.id;
const featureDescription = followUpFeature.description;
const prompt = followUpPrompt;
const imagePaths = followUpImagePaths.map(img => img.path);
console.log("[Board] Sending follow-up prompt for feature:", { console.log("[Board] Sending follow-up prompt for feature:", {
id: followUpFeature.id, id: featureId,
prompt: followUpPrompt, prompt: prompt,
imagePaths: followUpImagePaths imagePaths: imagePaths
}); });
try { const api = getElectronAPI();
const api = getElectronAPI(); if (!api?.autoMode?.followUpFeature) {
if (!api?.autoMode?.followUpFeature) { console.error("Follow-up feature API not available");
console.error("Follow-up feature API not available"); toast.error("Follow-up not available", {
toast.error("Follow-up not available", { description: "This feature is not available in the current version.",
description: "This feature is not available in the current version.", });
}); return;
return; }
}
// Move feature back to in_progress before sending follow-up // Move feature back to in_progress before sending follow-up
updateFeature(followUpFeature.id, { status: "in_progress", startedAt: new Date().toISOString() }); updateFeature(featureId, { status: "in_progress", startedAt: new Date().toISOString() });
// Call the API to send follow-up prompt // Reset follow-up state immediately (close dialog, clear form)
const result = await api.autoMode.followUpFeature( setShowFollowUpDialog(false);
currentProject.path, setFollowUpFeature(null);
followUpFeature.id, setFollowUpPrompt("");
followUpPrompt, setFollowUpImagePaths([]);
followUpImagePaths.map(img => img.path)
);
if (result.success) { // Show success toast immediately
console.log("[Board] Follow-up started successfully"); toast.success("Follow-up started", {
toast.success("Follow-up started", { description: `Continuing work on: ${featureDescription.slice(0, 50)}${featureDescription.length > 50 ? "..." : ""}`,
description: `Continuing work on: ${followUpFeature.description.slice(0, 50)}${followUpFeature.description.length > 50 ? "..." : ""}`, });
});
setShowFollowUpDialog(false); // Call the API in the background (don't await - let it run async)
setFollowUpFeature(null); api.autoMode.followUpFeature(
setFollowUpPrompt(""); currentProject.path,
setFollowUpImagePaths([]); featureId,
} else { prompt,
console.error("[Board] Failed to send follow-up:", result.error); imagePaths
toast.error("Failed to send follow-up", { ).catch((error) => {
description: result.error || "An error occurred",
});
await loadFeatures();
}
} catch (error) {
console.error("[Board] Error sending follow-up:", error); console.error("[Board] Error sending follow-up:", error);
toast.error("Failed to send follow-up", { toast.error("Failed to send follow-up", {
description: error instanceof Error ? error.message : "An error occurred", description: error instanceof Error ? error.message : "An error occurred",
}); });
await loadFeatures(); // Reload features to revert status if there was an error
} loadFeatures();
});
}; };
// Handle commit-only for waiting_approval features (marks as verified and commits) // Handle commit-only for waiting_approval features (marks as verified and commits)

View File

@@ -585,9 +585,11 @@ function createMockAutoModeAPI(): AutoModeAPI {
mockRunningFeatures.add(featureId); mockRunningFeatures.add(featureId);
// Simulate follow-up work (similar to run but with additional context) // Simulate follow-up work (similar to run but with additional context)
// Note: We don't await this - it runs in the background like the real implementation
simulateAutoModeLoop(projectPath, featureId); simulateAutoModeLoop(projectPath, featureId);
return { success: true, passes: true }; // Return immediately so the modal can close (matches real implementation)
return { success: true };
}, },
commitFeature: async (projectPath: string, featureId: string) => { commitFeature: async (projectPath: string, featureId: string) => {