Show warning if there's uncommitted changes (#92)

This commit is contained in:
Will Chen
2025-05-06 11:07:31 -07:00
committed by GitHub
parent 43ec6a4563
commit c425daf893
8 changed files with 65 additions and 10 deletions

View File

@@ -58,7 +58,10 @@ import { useVersions } from "@/hooks/useVersions";
import { useAttachments } from "@/hooks/useAttachments"; import { useAttachments } from "@/hooks/useAttachments";
import { AttachmentsList } from "./AttachmentsList"; import { AttachmentsList } from "./AttachmentsList";
import { DragDropOverlay } from "./DragDropOverlay"; import { DragDropOverlay } from "./DragDropOverlay";
import {
showError as showErrorToast,
showUncommittedFilesWarning,
} from "@/lib/toast";
const showTokenBarAtom = atom(false); const showTokenBarAtom = atom(false);
export function ChatInput({ chatId }: { chatId?: number }) { export function ChatInput({ chatId }: { chatId?: number }) {
@@ -189,6 +192,9 @@ export function ChatInput({ chatId }: { chatId?: number }) {
console.error("Failed to approve proposal:", result.error); console.error("Failed to approve proposal:", result.error);
setError(result.error || "Failed to approve proposal"); setError(result.error || "Failed to approve proposal");
} }
if (result.uncommittedFiles) {
showUncommittedFilesWarning(result.uncommittedFiles);
}
} catch (err) { } catch (err) {
console.error("Error approving proposal:", err); console.error("Error approving proposal:", err);
setError((err as Error)?.message || "An error occurred while approving"); setError((err as Error)?.message || "An error occurred while approving");

View File

@@ -14,7 +14,7 @@ import { useChats } from "./useChats";
import { useLoadApp } from "./useLoadApp"; import { useLoadApp } from "./useLoadApp";
import { selectedAppIdAtom } from "@/atoms/appAtoms"; import { selectedAppIdAtom } from "@/atoms/appAtoms";
import { useVersions } from "./useVersions"; import { useVersions } from "./useVersions";
import { showError } from "@/lib/toast"; import { showError, showUncommittedFilesWarning } from "@/lib/toast";
import { useProposal } from "./useProposal"; import { useProposal } from "./useProposal";
import { useSearch } from "@tanstack/react-router"; import { useSearch } from "@tanstack/react-router";
import { useRunApp } from "./useRunApp"; import { useRunApp } from "./useRunApp";
@@ -87,6 +87,9 @@ export function useStreamChat({
setIsPreviewOpen(true); setIsPreviewOpen(true);
refreshAppIframe(); refreshAppIframe();
} }
if (response.uncommittedFiles) {
showUncommittedFilesWarning(response.uncommittedFiles);
}
refreshProposal(chatId); refreshProposal(chatId);
// Keep the same as below // Keep the same as below

View File

@@ -486,6 +486,7 @@ This conversation includes one or more image attachments. When the user uploads
event.sender.send("chat:response:end", { event.sender.send("chat:response:end", {
chatId: req.chatId, chatId: req.chatId,
updatedFiles: status.updatedFiles ?? false, updatedFiles: status.updatedFiles ?? false,
uncommittedFiles: status.uncommittedFiles,
} satisfies ChatResponseEnd); } satisfies ChatResponseEnd);
} else { } else {
event.sender.send("chat:response:end", { event.sender.send("chat:response:end", {

View File

@@ -331,7 +331,11 @@ const getProposalHandler = async (
const approveProposalHandler = async ( const approveProposalHandler = async (
_event: IpcMainInvokeEvent, _event: IpcMainInvokeEvent,
{ chatId, messageId }: { chatId: number; messageId: number } { chatId, messageId }: { chatId: number; messageId: number }
): Promise<{ success: boolean; error?: string }> => { ): Promise<{
success: boolean;
error?: string;
uncommittedFiles?: string[];
}> => {
logger.log( logger.log(
`IPC: approve-proposal called for chatId: ${chatId}, messageId: ${messageId}` `IPC: approve-proposal called for chatId: ${chatId}, messageId: ${messageId}`
); );
@@ -380,7 +384,7 @@ const approveProposalHandler = async (
}; };
} }
return { success: true }; return { success: true, uncommittedFiles: processResult.uncommittedFiles };
} catch (error) { } catch (error) {
logger.error(`Error approving proposal for messageId ${messageId}:`, error); logger.error(`Error approving proposal for messageId ${messageId}:`, error);
return { return {

View File

@@ -111,10 +111,10 @@ export class IpcClient {
}); });
this.ipcRenderer.on("chat:response:end", (payload) => { this.ipcRenderer.on("chat:response:end", (payload) => {
const { chatId, updatedFiles } = payload as unknown as ChatResponseEnd; const { chatId } = payload as unknown as ChatResponseEnd;
const callbacks = this.chatStreams.get(chatId); const callbacks = this.chatStreams.get(chatId);
if (callbacks) { if (callbacks) {
callbacks.onEnd({ chatId, updatedFiles }); callbacks.onEnd(payload as unknown as ChatResponseEnd);
console.debug("chat:response:end"); console.debug("chat:response:end");
this.chatStreams.delete(chatId); this.chatStreams.delete(chatId);
} else { } else {
@@ -734,13 +734,21 @@ export class IpcClient {
}: { }: {
chatId: number; chatId: number;
messageId: number; messageId: number;
}): Promise<{ success: boolean; error?: string }> { }): Promise<{
success: boolean;
error?: string;
uncommittedFiles?: string[];
}> {
try { try {
const result = await this.ipcRenderer.invoke("approve-proposal", { const result = await this.ipcRenderer.invoke("approve-proposal", {
chatId, chatId,
messageId, messageId,
}); });
return result as { success: boolean; error?: string }; return result as {
success: boolean;
error?: string;
uncommittedFiles?: string[];
};
} catch (error) { } catch (error) {
showError(error); showError(error);
return { success: false, error: (error as Error).message }; return { success: false, error: (error as Error).message };

View File

@@ -24,6 +24,7 @@ export interface ChatStreamParams {
export interface ChatResponseEnd { export interface ChatResponseEnd {
chatId: number; chatId: number;
updatedFiles: boolean; updatedFiles: boolean;
uncommittedFiles?: string[];
} }
export interface CreateAppParams { export interface CreateAppParams {

View File

@@ -175,7 +175,11 @@ export async function processFullResponseActions(
chatSummary, chatSummary,
messageId, messageId,
}: { chatSummary: string | undefined; messageId: number } }: { chatSummary: string | undefined; messageId: number }
): Promise<{ updatedFiles?: boolean; error?: string }> { ): Promise<{
updatedFiles?: boolean;
error?: string;
uncommittedFiles?: string[];
}> {
logger.log("processFullResponseActions for chatId", chatId); logger.log("processFullResponseActions for chatId", chatId);
// Get the app associated with the chat // Get the app associated with the chat
const chatWithApp = await db.query.chats.findFirst({ const chatWithApp = await db.query.chats.findFirst({
@@ -453,6 +457,13 @@ export async function processFullResponseActions(
}) })
.where(eq(messages.id, messageId)); .where(eq(messages.id, messageId));
} }
// Check for any uncommitted changes after the commit
const statusMatrix = await git.statusMatrix({ fs, dir: appPath });
const uncommittedFiles = statusMatrix
.filter((row) => row[1] !== 1 || row[2] !== 1 || row[3] !== 1)
.map((row) => row[0]); // Get just the file paths
logger.log("mark as approved: hasChanges", hasChanges); logger.log("mark as approved: hasChanges", hasChanges);
// Update the message to approved // Update the message to approved
await db await db
@@ -461,7 +472,12 @@ export async function processFullResponseActions(
approvalState: "approved", approvalState: "approved",
}) })
.where(eq(messages.id, messageId)); .where(eq(messages.id, messageId));
return { updatedFiles: hasChanges };
return {
updatedFiles: hasChanges,
uncommittedFiles:
uncommittedFiles.length > 0 ? uncommittedFiles : undefined,
};
} catch (error: unknown) { } catch (error: unknown) {
logger.error("Error processing files:", error); logger.error("Error processing files:", error);
return { error: (error as any).toString() }; return { error: (error as any).toString() };

View File

@@ -21,6 +21,15 @@ export const showError = (message: any) => {
console.error(message); console.error(message);
}; };
/**
* Show a warning toast
* @param message The warning message to display
*/
export const showWarning = (message: string) => {
toast.warning(message);
console.warn(message);
};
/** /**
* Show an info toast * Show an info toast
* @param message The info message to display * @param message The info message to display
@@ -49,5 +58,12 @@ export const showLoading = <T>(
}); });
}; };
export const showUncommittedFilesWarning = (files: string[]) => {
showWarning(
`Some changed files were not committed. Please use git to manually commit them.
\n\n${files.join("\n")}`
);
};
// Re-export for direct use // Re-export for direct use
export { toast }; export { toast };