From ed183fb7523c36636ae5d2d83912c0b2e31bcfbd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eliezer=20Gonz=C3=A1lez?= Date: Sat, 25 Apr 2026 14:23:57 +0100 Subject: [PATCH 1/2] Require confirmation before closing a pinned tab --- docs/notes-3.7.txt | 5 +++++ .../AppShutdown/iTermPromptOnCloseReason.h | 1 + .../AppShutdown/iTermPromptOnCloseReason.m | 4 ++++ sources/TerminalView/PseudoTerminal.m | 21 +++++++++++++++++-- 4 files changed, 29 insertions(+), 2 deletions(-) diff --git a/docs/notes-3.7.txt b/docs/notes-3.7.txt index d5bdd598d7..29f9cdecb0 100644 --- a/docs/notes-3.7.txt +++ b/docs/notes-3.7.txt @@ -107,6 +107,11 @@ New Features in Terminal Sessions: Settings > Profiles > Text. Tabs, Windows, and UI: +- Closing a pinned tab now requires + confirmation, whether via the close + button, middle-click, ⌘W, or quitting + the app. The dialog identifies the tab + as pinned so the prompt is clear. - The control-sequence-controlled progress indicator is now shown in the tab bar. - An active pane border can be displayed to diff --git a/sources/AppShutdown/iTermPromptOnCloseReason.h b/sources/AppShutdown/iTermPromptOnCloseReason.h index 5646589a5b..38673cf71a 100644 --- a/sources/AppShutdown/iTermPromptOnCloseReason.h +++ b/sources/AppShutdown/iTermPromptOnCloseReason.h @@ -22,6 +22,7 @@ + (instancetype)closingMultipleSessionsPreferenceEnabled; + (instancetype)tmuxClientsAlwaysPromptBecauseJobsAreNotExposed; + (instancetype)sessionIsLocked; ++ (instancetype)tabIsPinned; - (void)addReason:(iTermPromptOnCloseReason *)reason; diff --git a/sources/AppShutdown/iTermPromptOnCloseReason.m b/sources/AppShutdown/iTermPromptOnCloseReason.m index 54b042029e..f731bf89b4 100644 --- a/sources/AppShutdown/iTermPromptOnCloseReason.m +++ b/sources/AppShutdown/iTermPromptOnCloseReason.m @@ -238,6 +238,10 @@ + (instancetype)sessionIsLocked { return [[[iTermPromptOnCloseMessageReason alloc] initWithMessage:@"This pane is locked." priority:75] autorelease]; } ++ (instancetype)tabIsPinned { + return [[[iTermPromptOnCloseMessageReason alloc] initWithMessage:@"A pinned tab is open." priority:70] autorelease]; +} + - (BOOL)hasReason { return YES; } diff --git a/sources/TerminalView/PseudoTerminal.m b/sources/TerminalView/PseudoTerminal.m index d311103262..1e041ec335 100644 --- a/sources/TerminalView/PseudoTerminal.m +++ b/sources/TerminalView/PseudoTerminal.m @@ -1941,14 +1941,21 @@ - (BOOL)confirmCloseTab:(PTYTab *)aTab suppressConfirmation:(BOOL)suppressConfir const BOOL anyIsLocked = [[aTab sessions] anyWithBlock:^BOOL(PTYSession *anObject) { return anObject.locked; }]; + NSString *const pinnedPrefix = aTab.isPinned ? @"pinned " : @""; if (numClosing == 1) { + NSString *const identifier = anyIsLocked + ? [NSString stringWithFormat:@"This %@tab (with a locked session)", pinnedPrefix] + : [NSString stringWithFormat:@"This %@tab", pinnedPrefix]; okToClose = [self confirmCloseForSessions:[aTab sessions] - identifier:anyIsLocked ? @"This tab (with a locked session)" : @"This tab" + identifier:identifier genericName:[NSString stringWithFormat:@"tab #%d", [aTab tabNumber]]]; } else { + NSString *const identifier = anyIsLocked + ? [NSString stringWithFormat:@"This %@multi-pane tab (with locked sessions)", pinnedPrefix] + : [NSString stringWithFormat:@"This %@multi-pane tab", pinnedPrefix]; okToClose = [self confirmCloseForSessions:[aTab sessions] - identifier:anyIsLocked ? @"This multi-pane tab (with locked sessions)" : @"This multi-pane tab" + identifier:identifier genericName:[NSString stringWithFormat:@"tab #%d", [aTab tabNumber]]]; } @@ -4175,6 +4182,12 @@ - (iTermPromptOnCloseReason *)promptOnCloseReason { for (PTYSession *aSession in [self allSessions]) { [reason addReason:[aSession promptOnCloseReason]]; } + for (PTYTab *tab in self.tabs) { + if (tab.isPinned) { + [reason addReason:[iTermPromptOnCloseReason tabIsPinned]]; + break; + } + } return reason; } @@ -6662,6 +6675,10 @@ - (void)menuForEvent:(NSEvent *)theEvent menu:(NSMenu *)theMenu // NSTabView - (void)tabView:(NSTabView *)tabView closeTab:(id)identifier button:(int)button { if (button != 2 || [iTermAdvancedSettingsModel middleClickClosesTab]) { + PTYTab *const tab = identifier; + if (tab.isPinned && ![self confirmCloseTab:tab suppressConfirmation:NO]) { + return; + } [self closeTab:identifier]; } } From 554fce415fb7fb4460c17bdb03434935cbab5b64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eliezer=20Gonz=C3=A1lez?= Date: Sat, 25 Apr 2026 16:24:40 +0100 Subject: [PATCH 2/2] Fix duplicate pinned-tab close warning on middle click The tab-bar close flow already runs shouldCloseTabViewItem before closeTab. Remove the redundant pinned-tab confirmation in closeTab:button: so pinned tabs prompt once (same behavior as Cmd+W). --- sources/TerminalView/PseudoTerminal.m | 4 ---- 1 file changed, 4 deletions(-) diff --git a/sources/TerminalView/PseudoTerminal.m b/sources/TerminalView/PseudoTerminal.m index 1e041ec335..ff29804fd2 100644 --- a/sources/TerminalView/PseudoTerminal.m +++ b/sources/TerminalView/PseudoTerminal.m @@ -6675,10 +6675,6 @@ - (void)menuForEvent:(NSEvent *)theEvent menu:(NSMenu *)theMenu // NSTabView - (void)tabView:(NSTabView *)tabView closeTab:(id)identifier button:(int)button { if (button != 2 || [iTermAdvancedSettingsModel middleClickClosesTab]) { - PTYTab *const tab = identifier; - if (tab.isPinned && ![self confirmCloseTab:tab suppressConfirmation:NO]) { - return; - } [self closeTab:identifier]; } }