From 09a342a67b03336643af502fbff4cc31609c3504 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Sun, 13 Jul 2025 11:38:25 +0200 Subject: [PATCH] feat - add chat lifecycle handler for shutdown veto (#255617) --- .../platform/menubar/electron-main/menubar.ts | 4 +- .../bulkEdit/browser/bulkEditService.ts | 9 +-- .../electron-browser/chat.contribution.ts | 59 +++++++++++++++++++ 3 files changed, 63 insertions(+), 9 deletions(-) diff --git a/src/vs/platform/menubar/electron-main/menubar.ts b/src/vs/platform/menubar/electron-main/menubar.ts index 1f87fc261c5..1a3c71d0be7 100644 --- a/src/vs/platform/menubar/electron-main/menubar.ts +++ b/src/vs/platform/menubar/electron-main/menubar.ts @@ -467,10 +467,10 @@ export class Menubar extends Disposable { const { response } = await this.nativeHostMainService.showMessageBox(this.windowsMainService.getFocusedWindow()?.id, { type: 'question', buttons: [ - nls.localize({ key: 'quit', comment: ['&& denotes a mnemonic'] }, "&&Quit"), + isMacintosh ? nls.localize({ key: 'quit', comment: ['&& denotes a mnemonic'] }, "&&Quit") : nls.localize({ key: 'exit', comment: ['&& denotes a mnemonic'] }, "&&Exit"), nls.localize('cancel', "Cancel") ], - message: nls.localize('quitMessage', "Are you sure you want to quit?") + message: isMacintosh ? nls.localize('quitMessageMac', "Are you sure you want to quit?") : nls.localize('quitMessage', "Are you sure you want to exit?") }); return response === 0; diff --git a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditService.ts b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditService.ts index 69683e9bdce..09d10c204df 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/bulkEditService.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/bulkEditService.ts @@ -30,6 +30,7 @@ import { ILifecycleService, ShutdownReason } from '../../../services/lifecycle/c import { IWorkingCopyService } from '../../../services/workingCopy/common/workingCopyService.js'; import { OpaqueEdits, ResourceAttachmentEdit } from './opaqueEdits.js'; import { TextModelEditReason } from '../../../../editor/common/textModelEditReason.js'; +import { isMacintosh } from '../../../../base/common/platform.js'; function liftEdits(edits: ResourceEdit[]): ResourceEdit[] { return edits.map(edit => { @@ -295,30 +296,24 @@ export class BulkEditService implements IBulkEditService { private async _shouldVeto(label: string | undefined, reason: ShutdownReason): Promise { let message: string; - let primaryButton: string; switch (reason) { case ShutdownReason.CLOSE: message = localize('closeTheWindow.message', "Are you sure you want to close the window?"); - primaryButton = localize({ key: 'closeTheWindow', comment: ['&& denotes a mnemonic'] }, "&&Close Window"); break; case ShutdownReason.LOAD: message = localize('changeWorkspace.message', "Are you sure you want to change the workspace?"); - primaryButton = localize({ key: 'changeWorkspace', comment: ['&& denotes a mnemonic'] }, "Change &&Workspace"); break; case ShutdownReason.RELOAD: message = localize('reloadTheWindow.message', "Are you sure you want to reload the window?"); - primaryButton = localize({ key: 'reloadTheWindow', comment: ['&& denotes a mnemonic'] }, "&&Reload Window"); break; default: - message = localize('quit.message', "Are you sure you want to quit?"); - primaryButton = localize({ key: 'quit', comment: ['&& denotes a mnemonic'] }, "&&Quit"); + message = isMacintosh ? localize('quitMessageMac', "Are you sure you want to quit?") : localize('quitMessage', "Are you sure you want to exit?"); break; } const result = await this._dialogService.confirm({ message, detail: localize('areYouSureQuiteBulkEdit.detail', "'{0}' is in progress.", label || localize('fileOperation', "File operation")), - primaryButton }); return !result.confirmed; diff --git a/src/vs/workbench/contrib/chat/electron-browser/chat.contribution.ts b/src/vs/workbench/contrib/chat/electron-browser/chat.contribution.ts index d3b943b273c..a0d3b700e7e 100644 --- a/src/vs/workbench/contrib/chat/electron-browser/chat.contribution.ts +++ b/src/vs/workbench/contrib/chat/electron-browser/chat.contribution.ts @@ -30,6 +30,9 @@ import { ViewContainerLocation } from '../../../common/views.js'; import { INativeHostService } from '../../../../platform/native/common/native.js'; import { IChatService } from '../common/chatService.js'; import { autorun } from '../../../../base/common/observable.js'; +import { ILifecycleService, ShutdownReason } from '../../../services/lifecycle/common/lifecycle.js'; +import { IDialogService } from '../../../../platform/dialogs/common/dialogs.js'; +import { isMacintosh } from '../../../../base/common/platform.js'; class NativeBuiltinToolsContribution extends Disposable implements IWorkbenchContribution { @@ -130,6 +133,61 @@ class ChatSuspendThrottlingHandler extends Disposable { } } +class ChatLifecycleHandler extends Disposable { + + static readonly ID = 'workbench.contrib.chatLifecycleHandler'; + + constructor( + @ILifecycleService lifecycleService: ILifecycleService, + @IChatService private readonly chatService: IChatService, + @IDialogService private readonly dialogService: IDialogService, + @IViewsService private readonly viewsService: IViewsService + ) { + super(); + + this._register(lifecycleService.onBeforeShutdown(e => { + e.veto(this.shouldVetoShutdown(e.reason), 'veto.chat'); + })); + } + + private shouldVetoShutdown(reason: ShutdownReason): boolean | Promise { + const running = this.chatService.requestInProgressObs.read(undefined); + if (!running) { + return false; + } + + return this.doShouldVetoShutdown(reason); + } + + private async doShouldVetoShutdown(reason: ShutdownReason): Promise { + + showChatView(this.viewsService); + + let message: string; + switch (reason) { + case ShutdownReason.CLOSE: + message = localize('closeTheWindow.message', "A chat request is in progress. Are you sure you want to close the window?"); + break; + case ShutdownReason.LOAD: + message = localize('changeWorkspace.message', "A chat request is in progress. Are you sure you want to change the workspace?"); + break; + case ShutdownReason.RELOAD: + message = localize('reloadTheWindow.message', "A chat request is in progress. Are you sure you want to reload the window?"); + break; + default: + message = isMacintosh ? localize('quit.message', "A chat request is in progress. Are you sure you want to quit?") : localize('exit.message', "A chat request is in progress. Are you sure you want to exit?"); + break; + } + + const result = await this.dialogService.confirm({ + message, + detail: localize('quit.detail', "The chat request will be cancelled if you continue.") + }); + + return !result.confirmed; + } +} + registerAction2(StartVoiceChatAction); registerAction2(InstallSpeechProviderForVoiceChatAction); @@ -151,3 +209,4 @@ registerWorkbenchContribution2(KeywordActivationContribution.ID, KeywordActivati registerWorkbenchContribution2(NativeBuiltinToolsContribution.ID, NativeBuiltinToolsContribution, WorkbenchPhase.AfterRestored); registerWorkbenchContribution2(ChatCommandLineHandler.ID, ChatCommandLineHandler, WorkbenchPhase.BlockRestore); registerWorkbenchContribution2(ChatSuspendThrottlingHandler.ID, ChatSuspendThrottlingHandler, WorkbenchPhase.AfterRestored); +registerWorkbenchContribution2(ChatLifecycleHandler.ID, ChatLifecycleHandler, WorkbenchPhase.AfterRestored);