Skip to content

Commit 810d7cb

Browse files
committed
feat(panel): add requestClose with onCloseRequested handler for panels
Add registerOnCloseRequestedHandler/requestClose API to both PanelView and PluginPanelView. The tab close button now calls requestClose() which invokes the async handler before hiding. Terminal registers a handler that confirms before disposing all terminals when active processes are running or multiple terminals are open.
1 parent 165b419 commit 810d7cb

4 files changed

Lines changed: 104 additions & 1 deletion

File tree

src/extensionsIntegrated/Terminal/main.js

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -586,6 +586,48 @@ define(function (require, exports, module) {
586586
_initNodeConnector();
587587
_createPanel();
588588

589+
// Gate user-initiated panel close (X button): confirm if needed, then
590+
// dispose all terminals. Programmatic hide() just collapses the panel
591+
// without disposing terminals.
592+
panel.registerOnCloseRequestedHandler(async function () {
593+
const activeProcesses = [];
594+
for (const inst of terminalInstances) {
595+
if (!inst.isAlive) {
596+
continue;
597+
}
598+
try {
599+
const result = await nodeConnector.execPeer("getTerminalProcess", {id: inst.id});
600+
if (result.process && !_isShellProcess(result.process)) {
601+
activeProcesses.push(result.process);
602+
}
603+
} catch (e) {
604+
// terminal may be dead
605+
}
606+
}
607+
608+
if (terminalInstances.length > 1 || activeProcesses.length > 0) {
609+
const msgKey = activeProcesses.length > 0
610+
? Strings.TERMINAL_CLOSE_ALL_MSG_PROCESS
611+
: Strings.TERMINAL_CLOSE_ALL_MSG;
612+
const message = StringUtils.format(
613+
msgKey, terminalInstances.length, activeProcesses.length
614+
);
615+
const dialog = Dialogs.showConfirmDialog(
616+
Strings.TERMINAL_CLOSE_ALL_TITLE, message
617+
);
618+
const buttonId = await dialog.getPromise();
619+
if (buttonId !== Dialogs.DIALOG_BTN_OK) {
620+
return false;
621+
}
622+
}
623+
624+
// User confirmed (or single idle terminal) — dispose everything
625+
_disposeAll();
626+
activeTerminalId = null;
627+
_updateFlyout();
628+
return true;
629+
});
630+
589631
// Detect shells
590632
ShellProfiles.init(nodeConnector).then(function () {
591633
const shells = ShellProfiles.getShells();

src/nls/root/strings.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1488,6 +1488,9 @@ define({
14881488
"ERROR_TERMINAL_NOT_FOUND": "Terminal was not found for your OS, you can define a custom Terminal command in the settings",
14891489
"TERMINAL_CLOSE_CONFIRM_TITLE": "Active Process Running",
14901490
"TERMINAL_CLOSE_CONFIRM_MSG": "Terminal has an active process running: <b>{0}</b>.<br>Are you sure you want to close it?",
1491+
"TERMINAL_CLOSE_ALL_TITLE": "Close All Terminals",
1492+
"TERMINAL_CLOSE_ALL_MSG": "This will close {0} terminal(s).<br>Continue?",
1493+
"TERMINAL_CLOSE_ALL_MSG_PROCESS": "This will close {0} terminal(s). {1} have active processes that will be terminated.<br>Continue?",
14911494
"TERMINAL_FOCUS_HINT": "Press {0} to switch between editor and terminal",
14921495
"EXTENDED_COMMIT_MESSAGE": "EXTENDED",
14931496
"GETTING_STAGED_DIFF_PROGRESS": "Getting diff of staged files\u2026",

src/view/PanelView.js

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -328,6 +328,35 @@ define(function (require, exports, module) {
328328
return true;
329329
};
330330

331+
/**
332+
* Registers an async handler that is called before the panel is closed via user interaction (e.g. clicking the
333+
* tab close button). The handler should return `true` to allow the close, or `false` to prevent it.
334+
* @param {function|null} handler An async function returning a boolean, or null to clear the handler.
335+
*/
336+
Panel.prototype.registerOnCloseRequestedHandler = function (handler) {
337+
if (this._onCloseRequestedHandler && handler) {
338+
console.warn(`onCloseRequestedHandler already registered for panel: ${this.panelID}. will be overwritten`);
339+
}
340+
this._onCloseRequestedHandler = handler;
341+
};
342+
343+
/**
344+
* Requests the panel to hide, invoking the registered onCloseRequested handler first (if any).
345+
* If the handler returns false, the panel stays open. If it returns true or no handler is
346+
* registered, `hide()` is called.
347+
* @return {Promise<boolean>} Resolves to true if the panel was hidden, false if prevented.
348+
*/
349+
Panel.prototype.requestClose = async function () {
350+
if (this._onCloseRequestedHandler) {
351+
const allowed = await this._onCloseRequestedHandler();
352+
if (!allowed) {
353+
return false;
354+
}
355+
}
356+
this.hide();
357+
return true;
358+
};
359+
331360
/**
332361
* Shows the panel
333362
*/
@@ -495,7 +524,7 @@ define(function (require, exports, module) {
495524
if (panelId) {
496525
let panel = _panelMap[panelId];
497526
if (panel) {
498-
panel.hide();
527+
panel.requestClose();
499528
}
500529
}
501530
});

src/view/PluginPanelView.js

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,35 @@ define(function (require, exports, module) {
109109
return true;
110110
};
111111

112+
/**
113+
* Registers an async handler that is called before the panel is closed via user interaction.
114+
* The handler should return `true` to allow the close, or `false` to prevent it.
115+
* @param {function|null} handler An async function returning a boolean, or null to clear the handler.
116+
*/
117+
Panel.prototype.registerOnCloseRequestedHandler = function (handler) {
118+
if (this._onCloseRequestedHandler && handler) {
119+
console.warn(`onCloseRequestedHandler already registered for panel: ${this.panelID}. will be overwritten`);
120+
}
121+
this._onCloseRequestedHandler = handler;
122+
};
123+
124+
/**
125+
* Requests the panel to hide, invoking the registered onCloseRequested handler first (if any).
126+
* If the handler returns false, the panel stays open. If it returns true or no handler is
127+
* registered, `hide()` is called.
128+
* @return {Promise<boolean>} Resolves to true if the panel was hidden, false if prevented.
129+
*/
130+
Panel.prototype.requestClose = async function () {
131+
if (this._onCloseRequestedHandler) {
132+
const allowed = await this._onCloseRequestedHandler();
133+
if (!allowed) {
134+
return false;
135+
}
136+
}
137+
this.hide();
138+
return true;
139+
};
140+
112141
/**
113142
* Shows the panel
114143
*/

0 commit comments

Comments
 (0)