From dff28cdfbb6993414e64735f871068567b4769d5 Mon Sep 17 00:00:00 2001 From: Pedram Amini Date: Mon, 2 Feb 2026 04:53:18 -0600 Subject: [PATCH] MAESTRO: Add unsaved changes warning for file tab close - Renamed handleCloseFileTab to forceCloseFileTab (performs unconditional close) - Added new handleCloseFileTab wrapper that checks for unsaved changes - When tab.editContent !== undefined, shows confirmation modal before closing - Uses existing showConfirmation pattern with setConfirmModal* state - Modal displays: "" has unsaved changes. Are you sure you want to close it? - On confirm, calls forceCloseFileTab to perform actual close --- src/renderer/App.tsx | 84 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 82 insertions(+), 2 deletions(-) diff --git a/src/renderer/App.tsx b/src/renderer/App.tsx index 63ca36d0..ed25d091 100644 --- a/src/renderer/App.tsx +++ b/src/renderer/App.tsx @@ -5374,11 +5374,12 @@ You are taking over this conversation. Based on the context above, provide a bri ); /** - * Close a file preview tab. Removes it from filePreviewTabs and unifiedTabOrder. + * Force close a file preview tab without confirmation. + * Removes it from filePreviewTabs and unifiedTabOrder. * If this was the active file tab, selects the next tab in unifiedTabOrder (could be AI or file). * Updates activeTabId or activeFileTabId accordingly. */ - const handleCloseFileTab = useCallback((tabId: string) => { + const forceCloseFileTab = useCallback((tabId: string) => { setSessions((prev) => prev.map((s) => { if (s.id !== activeSessionIdRef.current) return s; @@ -5448,6 +5449,83 @@ You are taking over this conversation. Based on the context above, provide a bri ); }, []); + /** + * Close a file preview tab with unsaved changes check. + * If the tab has unsaved changes (editContent !== undefined), show a confirmation modal. + * Otherwise, close the tab immediately. + */ + const handleCloseFileTab = useCallback( + (tabId: string) => { + // Find the tab to check for unsaved changes + const activeSession = sessions.find((s) => s.id === activeSessionIdRef.current); + if (!activeSession) { + forceCloseFileTab(tabId); + return; + } + + const tabToClose = activeSession.filePreviewTabs.find((tab) => tab.id === tabId); + if (!tabToClose) { + forceCloseFileTab(tabId); + return; + } + + // Check if tab has unsaved changes (editContent is set when different from saved content) + if (tabToClose.editContent !== undefined) { + // Show confirmation modal + setConfirmModalMessage( + `"${tabToClose.name}${tabToClose.extension}" has unsaved changes. Are you sure you want to close it?` + ); + setConfirmModalOnConfirm(() => () => { + forceCloseFileTab(tabId); + }); + setConfirmModalOpen(true); + } else { + // No unsaved changes, close immediately + forceCloseFileTab(tabId); + } + }, + [sessions, forceCloseFileTab, setConfirmModalMessage, setConfirmModalOnConfirm, setConfirmModalOpen] + ); + + /** + * Update a file tab's editMode state. + * Called when user toggles between edit and preview mode in FilePreview. + */ + const handleFileTabEditModeChange = useCallback((tabId: string, editMode: boolean) => { + setSessions((prev) => + prev.map((s) => { + if (s.id !== activeSessionIdRef.current) return s; + + const updatedFileTabs = s.filePreviewTabs.map((tab) => { + if (tab.id !== tabId) return tab; + return { ...tab, editMode }; + }); + + return { ...s, filePreviewTabs: updatedFileTabs }; + }) + ); + }, []); + + /** + * Update a file tab's editContent state. + * Called when user edits content in FilePreview. + * Pass undefined to indicate no pending changes (content matches saved file). + */ + const handleFileTabEditContentChange = useCallback((tabId: string, editContent: string | undefined) => { + setSessions((prev) => + prev.map((s) => { + if (s.id !== activeSessionIdRef.current) return s; + + const updatedFileTabs = s.filePreviewTabs.map((tab) => { + if (tab.id !== tabId) return tab; + return { ...tab, editContent }; + }); + + return { ...s, filePreviewTabs: updatedFileTabs }; + }) + ); + }, []); + /** * Select a file preview tab. This sets the file tab as active. * activeTabId is preserved to track the last active AI tab for when the user switches back. @@ -13050,6 +13128,8 @@ You are taking over this conversation. Based on the context above, provide a bri activeFileTab, handleFileTabSelect: handleSelectFileTab, handleFileTabClose: handleCloseFileTab, + handleFileTabEditModeChange, + handleFileTabEditContentChange, handleScrollPositionChange, handleAtBottomChange,