diff --git a/src/vs/base/browser/ui/dialog/dialog.ts b/src/vs/base/browser/ui/dialog/dialog.ts index 35d0ddc2db7..79129138217 100644 --- a/src/vs/base/browser/ui/dialog/dialog.ts +++ b/src/vs/base/browser/ui/dialog/dialog.ts @@ -34,6 +34,8 @@ export interface IDialogOptions { readonly type?: 'none' | 'info' | 'error' | 'question' | 'warning' | 'pending'; readonly inputs?: IDialogInputOptions[]; readonly keyEventProcessor?: (event: StandardKeyboardEvent) => void; + readonly renderBody?: (container: HTMLElement) => void; + readonly icon?: Codicon; } export interface IDialogResult { @@ -97,14 +99,23 @@ export class Dialog extends Disposable { this.iconElement = messageRowElement.appendChild($('.dialog-icon')); const messageContainer = messageRowElement.appendChild($('.dialog-message-container')); - if (this.options.detail) { + if (this.options.detail || this.options.renderBody) { const messageElement = messageContainer.appendChild($('.dialog-message')); const messageTextElement = messageElement.appendChild($('.dialog-message-text')); messageTextElement.innerText = this.message; } this.messageDetailElement = messageContainer.appendChild($('.dialog-message-detail')); - this.messageDetailElement.innerText = this.options.detail ? this.options.detail : message; + if (this.options.detail || !this.options.renderBody) { + this.messageDetailElement.innerText = this.options.detail ? this.options.detail : message; + } else { + this.messageDetailElement.style.display = 'none'; + } + + if (this.options.renderBody) { + const customBody = messageContainer.appendChild($('.dialog-message-body')); + this.options.renderBody(customBody); + } if (this.options.inputs) { this.inputs = this.options.inputs.map(input => { @@ -313,22 +324,26 @@ export class Dialog extends Disposable { this.iconElement.classList.remove(...dialogErrorIcon.classNamesArray, ...dialogWarningIcon.classNamesArray, ...dialogInfoIcon.classNamesArray, ...Codicon.loading.classNamesArray, spinModifierClassName); - switch (this.options.type) { - case 'error': - this.iconElement.classList.add(...dialogErrorIcon.classNamesArray); - break; - case 'warning': - this.iconElement.classList.add(...dialogWarningIcon.classNamesArray); - break; - case 'pending': - this.iconElement.classList.add(...Codicon.loading.classNamesArray, spinModifierClassName); - break; - case 'none': - case 'info': - case 'question': - default: - this.iconElement.classList.add(...dialogInfoIcon.classNamesArray); - break; + if (this.options.icon) { + this.iconElement.classList.add(...this.options.icon.classNamesArray); + } else { + switch (this.options.type) { + case 'error': + this.iconElement.classList.add(...dialogErrorIcon.classNamesArray); + break; + case 'warning': + this.iconElement.classList.add(...dialogWarningIcon.classNamesArray); + break; + case 'pending': + this.iconElement.classList.add(...Codicon.loading.classNamesArray, spinModifierClassName); + break; + case 'none': + case 'info': + case 'question': + default: + this.iconElement.classList.add(...dialogInfoIcon.classNamesArray); + break; + } } const actionBar = this._register(new ActionBar(this.toolbarContainer, {})); diff --git a/src/vs/platform/dialogs/common/dialogs.ts b/src/vs/platform/dialogs/common/dialogs.ts index 08c1a23a165..7aa108fa9c8 100644 --- a/src/vs/platform/dialogs/common/dialogs.ts +++ b/src/vs/platform/dialogs/common/dialogs.ts @@ -9,6 +9,8 @@ import { URI } from 'vs/base/common/uri'; import { basename } from 'vs/base/common/resources'; import { localize } from 'vs/nls'; import { ITelemetryData } from 'vs/platform/telemetry/common/telemetry'; +import { Codicon } from 'vs/base/common/codicons'; +import { IMarkdownString } from 'vs/base/common/htmlContent'; export interface FileFilter { extensions: string[]; @@ -178,11 +180,22 @@ export interface IOpenDialogOptions { export const IDialogService = createDecorator('dialogService'); +export interface ICustomDialogOptions { + markdownDetails?: ICustomDialogMarkdown[] + classes?: string[]; + icon?: Codicon; +} + +export interface ICustomDialogMarkdown { + markdown: IMarkdownString, + classes?: string[] +} + export interface IDialogOptions { cancelId?: number; detail?: string; checkbox?: ICheckbox; - useCustom?: boolean; + custom?: boolean | ICustomDialogOptions; } export interface IInput { diff --git a/src/vs/workbench/api/browser/mainThreadMessageService.ts b/src/vs/workbench/api/browser/mainThreadMessageService.ts index 995d6532930..f61b2cd5ec0 100644 --- a/src/vs/workbench/api/browser/mainThreadMessageService.ts +++ b/src/vs/workbench/api/browser/mainThreadMessageService.ts @@ -121,7 +121,7 @@ export class MainThreadMessageService implements MainThreadMessageServiceShape { cancelId = buttons.length - 1; } - const { choice } = await this._dialogService.show(severity, message, buttons, { cancelId, useCustom }); + const { choice } = await this._dialogService.show(severity, message, buttons, { cancelId, custom: useCustom }); return choice === commands.length ? undefined : commands[choice].handle; } } diff --git a/src/vs/workbench/browser/parts/dialogs/dialog.web.contribution.ts b/src/vs/workbench/browser/parts/dialogs/dialog.web.contribution.ts index 836303dbf81..719871a4ccc 100644 --- a/src/vs/workbench/browser/parts/dialogs/dialog.web.contribution.ts +++ b/src/vs/workbench/browser/parts/dialogs/dialog.web.contribution.ts @@ -17,6 +17,7 @@ import { BrowserDialogHandler } from 'vs/workbench/browser/parts/dialogs/dialogH import { DialogService } from 'vs/workbench/services/dialogs/common/dialogService'; import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { Disposable } from 'vs/base/common/lifecycle'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; export class DialogHandlerContribution extends Disposable implements IWorkbenchContribution { private readonly model: IDialogsModel; @@ -30,12 +31,13 @@ export class DialogHandlerContribution extends Disposable implements IWorkbenchC @ILayoutService layoutService: ILayoutService, @IThemeService themeService: IThemeService, @IKeybindingService keybindingService: IKeybindingService, + @IInstantiationService instantiationService: IInstantiationService, @IProductService productService: IProductService, @IClipboardService clipboardService: IClipboardService ) { super(); - this.impl = new BrowserDialogHandler(logService, layoutService, themeService, keybindingService, productService, clipboardService); + this.impl = new BrowserDialogHandler(logService, layoutService, themeService, keybindingService, instantiationService, productService, clipboardService); this.model = (this.dialogService as DialogService).model; diff --git a/src/vs/workbench/browser/parts/dialogs/dialogHandler.ts b/src/vs/workbench/browser/parts/dialogs/dialogHandler.ts index babf68f0dfa..41b06a0cea7 100644 --- a/src/vs/workbench/browser/parts/dialogs/dialogHandler.ts +++ b/src/vs/workbench/browser/parts/dialogs/dialogHandler.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { localize } from 'vs/nls'; -import { IDialogOptions, IConfirmation, IConfirmationResult, DialogType, IShowResult, IInputResult, ICheckbox, IInput, IDialogHandler } from 'vs/platform/dialogs/common/dialogs'; +import { IDialogOptions, IConfirmation, IConfirmationResult, DialogType, IShowResult, IInputResult, ICheckbox, IInput, IDialogHandler, ICustomDialogOptions } from 'vs/platform/dialogs/common/dialogs'; import { ILayoutService } from 'vs/platform/layout/browser/layoutService'; import { ILogService } from 'vs/platform/log/common/log'; import Severity from 'vs/base/common/severity'; @@ -18,6 +18,8 @@ import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IProductService } from 'vs/platform/product/common/productService'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; import { fromNow } from 'vs/base/common/date'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { MarkdownRenderer } from 'vs/editor/browser/core/markdownRenderer'; export class BrowserDialogHandler implements IDialogHandler { @@ -30,14 +32,19 @@ export class BrowserDialogHandler implements IDialogHandler { 'editor.action.clipboardPasteAction' ]; + private readonly markdownRenderer: MarkdownRenderer; + constructor( @ILogService private readonly logService: ILogService, @ILayoutService private readonly layoutService: ILayoutService, @IThemeService private readonly themeService: IThemeService, @IKeybindingService private readonly keybindingService: IKeybindingService, + @IInstantiationService private readonly instantiationService: IInstantiationService, @IProductService private readonly productService: IProductService, @IClipboardService private readonly clipboardService: IClipboardService - ) { } + ) { + this.markdownRenderer = this.instantiationService.createInstance(MarkdownRenderer, {}); + } async confirm(confirmation: IConfirmation): Promise { this.logService.trace('DialogService#confirm', confirmation.message); @@ -67,7 +74,7 @@ export class BrowserDialogHandler implements IDialogHandler { async show(severity: Severity, message: string, buttons: string[], options?: IDialogOptions): Promise { this.logService.trace('DialogService#show', message); - const result = await this.doShow(this.getDialogType(severity), message, buttons, options?.detail, options?.cancelId, options?.checkbox); + const result = await this.doShow(this.getDialogType(severity), message, buttons, options?.detail, options?.cancelId, options?.checkbox, undefined, typeof options?.custom === 'object' ? options.custom : undefined); return { choice: result.button, @@ -75,8 +82,19 @@ export class BrowserDialogHandler implements IDialogHandler { }; } - private async doShow(type: 'none' | 'info' | 'error' | 'question' | 'warning' | 'pending' | undefined, message: string, buttons: string[], detail?: string, cancelId?: number, checkbox?: ICheckbox, inputs?: IInput[]): Promise { + private async doShow(type: 'none' | 'info' | 'error' | 'question' | 'warning' | 'pending' | undefined, message: string, buttons: string[], detail?: string, cancelId?: number, checkbox?: ICheckbox, inputs?: IInput[], customOptions?: ICustomDialogOptions): Promise { const dialogDisposables = new DisposableStore(); + + const renderBody = customOptions ? (parent: HTMLElement) => { + parent.classList.add(...(customOptions.classes || [])); + (customOptions.markdownDetails || []).forEach(markdownDetail => { + const result = this.markdownRenderer.render(markdownDetail.markdown); + parent.appendChild(result.element); + result.element.classList.add(...(markdownDetail.classes || [])); + dialogDisposables.add(result); + }); + } : undefined; + const dialog = new Dialog( this.layoutService.container, message, @@ -93,6 +111,8 @@ export class BrowserDialogHandler implements IDialogHandler { } } }, + renderBody, + icon: customOptions?.icon, checkboxLabel: checkbox?.label, checkboxChecked: checkbox?.checked, inputs diff --git a/src/vs/workbench/contrib/remote/browser/remote.ts b/src/vs/workbench/contrib/remote/browser/remote.ts index 97ef48c3ed8..caa1edfe1cf 100644 --- a/src/vs/workbench/contrib/remote/browser/remote.ts +++ b/src/vs/workbench/contrib/remote/browser/remote.ts @@ -921,7 +921,7 @@ class RemoteAgentConnectionStatusListener extends Disposable implements IWorkben console.log(`Error handled: Not showing a notification for the error.`); } else if (!this._reloadWindowShown) { this._reloadWindowShown = true; - dialogService.show(Severity.Error, nls.localize('reconnectionPermanentFailure', "Cannot reconnect. Please reload the window."), [nls.localize('reloadWindow', "Reload Window"), nls.localize('cancel', "Cancel")], { cancelId: 1, useCustom: true }).then(result => { + dialogService.show(Severity.Error, nls.localize('reconnectionPermanentFailure', "Cannot reconnect. Please reload the window."), [nls.localize('reloadWindow', "Reload Window"), nls.localize('cancel', "Cancel")], { cancelId: 1, custom: true }).then(result => { // Reload the window if (result.choice === 0) { commandService.executeCommand(ReloadWindowAction.ID); diff --git a/src/vs/workbench/contrib/workspace/browser/media/trusted-badge.png b/src/vs/workbench/contrib/workspace/browser/media/trusted-badge.png new file mode 100644 index 00000000000..c53b0eea12f Binary files /dev/null and b/src/vs/workbench/contrib/workspace/browser/media/trusted-badge.png differ diff --git a/src/vs/workbench/contrib/workspace/browser/media/untrusted-status.png b/src/vs/workbench/contrib/workspace/browser/media/untrusted-status.png new file mode 100644 index 00000000000..c8b4f6ae7c8 Binary files /dev/null and b/src/vs/workbench/contrib/workspace/browser/media/untrusted-status.png differ diff --git a/src/vs/workbench/contrib/workspace/browser/workspace.contribution.ts b/src/vs/workbench/contrib/workspace/browser/workspace.contribution.ts index 9f1f7b779a2..783952398f3 100644 --- a/src/vs/workbench/contrib/workspace/browser/workspace.contribution.ts +++ b/src/vs/workbench/contrib/workspace/browser/workspace.contribution.ts @@ -36,7 +36,10 @@ import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storag import { isWeb } from 'vs/base/common/platform'; import { IsWebContext } from 'vs/platform/contextkey/common/contextkeys'; import { dirname, resolve } from 'vs/base/common/path'; +import product from 'vs/platform/product/common/product'; +import { FileAccess } from 'vs/base/common/network'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { MarkdownString } from 'vs/base/common/htmlContent'; const workspaceTrustIcon = registerIcon('workspace-trust-icon', Codicon.shield, localize('workspaceTrustIcon', "Icon for workspace trust badge.")); @@ -45,7 +48,7 @@ const workspaceTrustIcon = registerIcon('workspace-trust-icon', Codicon.shield, */ export class WorkspaceTrustRequestHandler extends Disposable implements IWorkbenchContribution { private readonly badgeDisposable = this._register(new MutableDisposable()); - private shouldShowManagementEditor = true; + private shouldShowIntroduction = true; constructor( @IDialogService private readonly dialogService: IDialogService, @@ -71,11 +74,63 @@ export class WorkspaceTrustRequestHandler extends Disposable implements IWorkben priority: 10 }); - const managementEditorShownKey = 'workspace.trust.management.shown'; - const seen = this.storageService.getBoolean(managementEditorShownKey, StorageScope.WORKSPACE, false); - if (!seen && this.shouldShowManagementEditor) { - this.storageService.store(managementEditorShownKey, true, StorageScope.WORKSPACE, StorageTarget.MACHINE); - this.commandService.executeCommand('workbench.trust.manage'); + const workspaceTrustIntroDialogDoNotShowAgainKey = 'workspace.trust.introduction.doNotShowAgain'; + const doNotShowAgain = this.storageService.getBoolean(workspaceTrustIntroDialogDoNotShowAgainKey, StorageScope.GLOBAL, false); + if (!doNotShowAgain && this.shouldShowIntroduction) { + // Show welcome dialog + (async () => { + + const result = await this.dialogService.show( + Severity.Info, + localize('workspaceTrust', "Introducing Workspace Trust"), + [ + localize('manageTrust', "Manage"), + localize('close', "Close") + ], + { + custom: { + icon: Codicon.shield, + classes: ['workspace-trust-intro-dialog'], + markdownDetails: [ + { + markdown: new MarkdownString(localize('workspaceTrustDescription', "{0} provides many powerful features that rely on the files that are open in the current workspace. This can mean unintended code execution from the workspace and should only happen if you trust the source of the files you have open.", 'VS Code' || product.nameShort)), + }, + { + markdown: new MarkdownString(`![${localize('altTextTrustedBadge', "Shield Badge on Activity Bar")}](${FileAccess.asBrowserUri('vs/workbench/contrib/workspace/browser/media/trusted-badge.png', require).toString(true)})\n*${localize('workspaceTrustBadgeDescription', "When features are disabled in an untrusted workspace, you will see this shield icon in the Activity Bar.")}*`), + classes: ['workspace-trust-dialog-image-row', 'badge-row'] + }, + { + markdown: new MarkdownString(`![${localize('altTextUntrustedStatus', "Workspace Trust Status Bar Entry")}](${FileAccess.asBrowserUri('vs/workbench/contrib/workspace/browser/media/untrusted-status.png', require).toString(true)})\n*${localize('workspaceTrustUntrustedDescription', "When the workspace is untrusted, you will see this status bar entry. It is hidden when the workspace is trusted.")}*`), + classes: ['workspace-trust-dialog-image-row', 'status-bar'] + }, + { + markdown: new MarkdownString(localize('seeTheDocs', "Manage your Workspace Trust configuration now to learn more or [see our docs for additional information](https://aka.ms/vscode-workspace-trust).")), + } + ], + }, + cancelId: 1, // Close + checkbox: { + label: localize('dontShowAgain', "Don't show this message again"), + checked: false, + } + } + ); + + // Dialog result + switch (result.choice) { + case 0: + this.workspaceTrustRequestService.cancelRequest(); + await this.commandService.executeCommand('workbench.trust.manage'); + break; + case 1: + this.workspaceTrustRequestService.completeRequest(undefined); + break; + } + + if (result.checkboxChecked) { + this.storageService.store(workspaceTrustIntroDialogDoNotShowAgainKey, true, StorageScope.GLOBAL, StorageTarget.USER); + } + })(); } } } @@ -101,13 +156,13 @@ export class WorkspaceTrustRequestHandler extends Disposable implements IWorkben // Dialog const result = await this.dialogService.show( - Severity.Warning, + Severity.Info, localize('immediateTrustRequestTitle', "Do you trust the files in this folder?"), buttons.map(b => b.label), { cancelId: buttons.findIndex(b => b.type === 'Cancel'), detail: localize('immediateTrustRequestDetail', "{0}\n\nYou should only trust this workspace if you trust its source. Using an untrusted workspace may compromise your device or personal information.", message), - useCustom: true + custom: { icon: Codicon.shield } } ); @@ -151,15 +206,19 @@ export class WorkspaceTrustRequestHandler extends Disposable implements IWorkben if (trusted && (e.changes.added.length || e.changes.changed.length)) { const addedFoldersTrustStateInfo = e.changes.added.map(folder => this.workspaceTrustStorageService.getFolderTrustStateInfo(folder.uri)); if (!addedFoldersTrustStateInfo.map(i => i.trusted).every(trusted => trusted)) { - const result = await this.dialogService.confirm({ - message: localize('addWorkspaceFolderMessage', "Do you trust the files in this folder?"), - detail: localize('addWorkspaceFolderDetail', "You are adding files to a trusted workspace that are not currently trusted. Do you want to trust the new files?"), - primaryButton: localize('yes', 'Yes'), - secondaryButton: localize('no', 'No') - }); + const result = await this.dialogService.show( + Severity.Info, + localize('addWorkspaceFolderMessage', "Do you trust the files in this folder?"), + [localize('yes', 'Yes'), localize('no', 'No')], + { + detail: localize('addWorkspaceFolderDetail', "You are adding files to a trusted workspace that are not currently trusted. Do you want to trust the new files?"), + cancelId: 1, + custom: { icon: Codicon.shield } + } + ); // Mark added/changed folders as trusted - this.workspaceTrustStorageService.setFoldersTrust(addedFoldersTrustStateInfo.map(i => i.uri), result.confirmed); + this.workspaceTrustStorageService.setFoldersTrust(addedFoldersTrustStateInfo.map(i => i.uri), result.choice === 0); resolve(); } @@ -170,7 +229,7 @@ export class WorkspaceTrustRequestHandler extends Disposable implements IWorkben })); // Don't auto-show the UX editor if the request is 5 seconds after startup - setTimeout(() => { this.shouldShowManagementEditor = false; }, 5000); + setTimeout(() => { this.shouldShowIntroduction = false; }, 5000); } } diff --git a/src/vs/workbench/contrib/workspace/browser/workspaceTrustEditor.css b/src/vs/workbench/contrib/workspace/browser/workspaceTrustEditor.css index b263addb44b..0fda6179da4 100644 --- a/src/vs/workbench/contrib/workspace/browser/workspaceTrustEditor.css +++ b/src/vs/workbench/contrib/workspace/browser/workspaceTrustEditor.css @@ -237,3 +237,23 @@ margin-left: 4px; min-width: 20%; } + +.workspace-trust-intro-dialog { + min-width: min(50vw, 500px); + padding-right: 24px; +} + +.workspace-trust-intro-dialog .workspace-trust-dialog-image-row p { + display: flex; + align-items: center; +} + +.workspace-trust-intro-dialog .workspace-trust-dialog-image-row.badge-row img { + max-height: 40px; + padding-right: 10px; +} + +.workspace-trust-intro-dialog .workspace-trust-dialog-image-row.status-bar img { + max-height: 32px; + padding-right: 10px; +} diff --git a/src/vs/workbench/contrib/workspace/browser/workspaceTrustEditor.ts b/src/vs/workbench/contrib/workspace/browser/workspaceTrustEditor.ts index bfa6ce55b00..58d3b994f23 100644 --- a/src/vs/workbench/contrib/workspace/browser/workspaceTrustEditor.ts +++ b/src/vs/workbench/contrib/workspace/browser/workspaceTrustEditor.ts @@ -23,7 +23,7 @@ import { IContextMenuService } from 'vs/platform/contextview/browser/contextView import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; import { ExtensionWorkspaceTrustRequestType } from 'vs/platform/extensions/common/extensions'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { IPromptChoiceWithMenu } from 'vs/platform/notification/common/notification'; +import { IPromptChoiceWithMenu, Severity } from 'vs/platform/notification/common/notification'; import { Link } from 'vs/platform/opener/browser/link'; import { IStorageService } from 'vs/platform/storage/common/storage'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; @@ -392,8 +392,8 @@ export class WorkspaceTrustEditor extends EditorPane { const primaryButton = localize('workspaceTrustTransitionPrimaryButton', "Yes"); const secondaryButton = localize('workspaceTrustTransitionSecondaryButton', "No"); - const result = await this.dialogService.confirm({ type: 'info', message, detail, primaryButton, secondaryButton }); - if (!result.confirmed) { + const result = await this.dialogService.show(Severity.Info, message, [primaryButton, secondaryButton], { cancelId: 1, detail, custom: { icon: Codicon.shield } }); + if (result.choice !== 0) { return; } } diff --git a/src/vs/workbench/electron-sandbox/parts/dialogs/dialog.contribution.ts b/src/vs/workbench/electron-sandbox/parts/dialogs/dialog.contribution.ts index 1d117bd14f3..ea707f18a2b 100644 --- a/src/vs/workbench/electron-sandbox/parts/dialogs/dialog.contribution.ts +++ b/src/vs/workbench/electron-sandbox/parts/dialogs/dialog.contribution.ts @@ -20,6 +20,7 @@ import { NativeDialogHandler } from 'vs/workbench/electron-sandbox/parts/dialogs import { DialogService } from 'vs/workbench/services/dialogs/common/dialogService'; import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { Disposable } from 'vs/base/common/lifecycle'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; export class DialogHandlerContribution extends Disposable implements IWorkbenchContribution { private nativeImpl: IDialogHandler; @@ -35,13 +36,14 @@ export class DialogHandlerContribution extends Disposable implements IWorkbenchC @ILayoutService layoutService: ILayoutService, @IThemeService themeService: IThemeService, @IKeybindingService keybindingService: IKeybindingService, + @IInstantiationService instantiationService: IInstantiationService, @IProductService productService: IProductService, @IClipboardService clipboardService: IClipboardService, @INativeHostService nativeHostService: INativeHostService ) { super(); - this.browserImpl = new BrowserDialogHandler(logService, layoutService, themeService, keybindingService, productService, clipboardService); + this.browserImpl = new BrowserDialogHandler(logService, layoutService, themeService, keybindingService, instantiationService, productService, clipboardService); this.nativeImpl = new NativeDialogHandler(logService, nativeHostService, productService, clipboardService); this.model = (this.dialogService as DialogService).model; @@ -76,7 +78,7 @@ export class DialogHandlerContribution extends Disposable implements IWorkbenchC // Message else if (this.currentDialog.args.showArgs) { const args = this.currentDialog.args.showArgs; - result = this.useCustomDialog || args.options?.useCustom ? + result = (this.useCustomDialog || args.options?.custom) ? await this.browserImpl.show(args.severity, args.message, args.buttons, args.options) : await this.nativeImpl.show(args.severity, args.message, args.buttons, args.options); }