Skip to content

Commit 837fc86

Browse files
Steven Voclaude
authored andcommitted
Add confirmation dialog before closing tabs
Prevents accidental tab closures by showing a confirmation modal when clicking the X button or selecting 'Close Tab' from menu. - Created ConfirmCloseTabModal component - Registered modal in modal registry - Modified handleCloseTab to show confirmation - Modal has OK (closes tab) and Cancel (keeps tab) buttons Addresses common UX issue where users accidentally close tabs. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
1 parent fbb0c4d commit 837fc86

File tree

3 files changed

+44
-4
lines changed

3 files changed

+44
-4
lines changed
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// Copyright 2025, Command Line Inc.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
import { Modal } from "@/app/modals/modal";
5+
import { deleteLayoutModelForTab } from "@/layout/index";
6+
import { atoms, getApi, globalStore } from "@/store/global";
7+
import { modalsModel } from "@/store/modalmodel";
8+
9+
interface ConfirmCloseTabModalProps {
10+
tabId: string;
11+
}
12+
13+
const ConfirmCloseTabModal = ({ tabId }: ConfirmCloseTabModalProps) => {
14+
const handleConfirmClose = () => {
15+
const ws = globalStore.get(atoms.workspace);
16+
getApi().closeTab(ws.oid, tabId);
17+
deleteLayoutModelForTab(tabId);
18+
modalsModel.popModal();
19+
};
20+
21+
const handleCancel = () => {
22+
modalsModel.popModal();
23+
};
24+
25+
return (
26+
<Modal onOk={handleConfirmClose} onCancel={handleCancel} onClose={handleCancel}>
27+
<div className="content">
28+
<div className="modal-title">Close Tab?</div>
29+
<div style={{ marginTop: "10px" }}>
30+
Are you sure you want to close this tab? This action cannot be undone.
31+
</div>
32+
</div>
33+
</Modal>
34+
);
35+
};
36+
37+
ConfirmCloseTabModal.displayName = "ConfirmCloseTabModal";
38+
39+
export { ConfirmCloseTabModal };

frontend/app/modals/modalregistry.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { UpgradeOnboardingModal } from "@/app/onboarding/onboarding-upgrade";
77
import { DeleteFileModal, PublishAppModal, RenameFileModal } from "@/builder/builder-apppanel";
88
import { SetSecretDialog } from "@/builder/tabs/builder-secrettab";
99
import { AboutModal } from "./about";
10+
import { ConfirmCloseTabModal } from "./confirmclosetab";
1011
import { UserInputModal } from "./userinputmodal";
1112

1213
const modalRegistry: { [key: string]: React.ComponentType<any> } = {
@@ -15,6 +16,7 @@ const modalRegistry: { [key: string]: React.ComponentType<any> } = {
1516
[UserInputModal.displayName || "UserInputModal"]: UserInputModal,
1617
[AboutModal.displayName || "AboutModal"]: AboutModal,
1718
[MessageModal.displayName || "MessageModal"]: MessageModal,
19+
[ConfirmCloseTabModal.displayName || "ConfirmCloseTabModal"]: ConfirmCloseTabModal,
1820
[PublishAppModal.displayName || "PublishAppModal"]: PublishAppModal,
1921
[RenameFileModal.displayName || "RenameFileModal"]: RenameFileModal,
2022
[DeleteFileModal.displayName || "DeleteFileModal"]: DeleteFileModal,

frontend/app/tab/tabbar.tsx

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -620,10 +620,9 @@ const TabBar = memo(({ workspace }: TabBarProps) => {
620620

621621
const handleCloseTab = (event: React.MouseEvent<HTMLButtonElement, MouseEvent> | null, tabId: string) => {
622622
event?.stopPropagation();
623-
const ws = globalStore.get(atoms.workspace);
624-
getApi().closeTab(ws.oid, tabId);
625-
tabsWrapperRef.current.style.setProperty("--tabs-wrapper-transition", "width 0.3s ease");
626-
deleteLayoutModelForTab(tabId);
623+
624+
// Show confirmation modal before closing
625+
modalsModel.pushModal("ConfirmCloseTabModal", { tabId });
627626
};
628627

629628
const handlePinChange = useCallback(

0 commit comments

Comments
 (0)