diff --git a/src/vs/code/browser/workbench/workbench.ts b/src/vs/code/browser/workbench/workbench.ts index c629f7fffa1faad78e5d9907fd38aec289db0428..2c3adf03e364317e7af01a7370b1bc83ae122413 100644 --- a/src/vs/code/browser/workbench/workbench.ts +++ b/src/vs/code/browser/workbench/workbench.ts @@ -3,7 +3,10 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IWorkbenchConstructionOptions, create, URI, Emitter, UriComponents, ICredentialsProvider, IURLCallbackProvider, IWorkspaceProvider, IWorkspace } from 'vs/workbench/workbench.web.api'; +import { IWorkbenchConstructionOptions, create, ICredentialsProvider, IURLCallbackProvider, IWorkspaceProvider, IWorkspace, IRemoteIndicator, ICommand, IHomeIndicator } from 'vs/workbench/workbench.web.api'; +import product from 'vs/platform/product/common/product'; +import { URI, UriComponents } from 'vs/base/common/uri'; +import { Event, Emitter } from 'vs/base/common/event'; import { generateUuid } from 'vs/base/common/uuid'; import { CancellationToken } from 'vs/base/common/cancellation'; import { streamToBuffer } from 'vs/base/common/buffer'; @@ -276,6 +279,51 @@ class WorkspaceProvider implements IWorkspaceProvider { } } +class RemoteIndicator implements IRemoteIndicator { + + readonly onDidChange = Event.None; + + readonly label: string; + readonly tooltip: string; + readonly command: string | undefined; + + readonly commandImpl: ICommand | undefined = undefined; + + constructor(workspace: IWorkspace) { + let repositoryOwner: string | undefined = undefined; + let repositoryName: string | undefined = undefined; + + if (workspace) { + let uri: URI | undefined = undefined; + if (isFolderToOpen(workspace)) { + uri = workspace.folderUri; + } else if (isWorkspaceToOpen(workspace)) { + uri = workspace.workspaceUri; + } + + if (uri?.scheme === 'github' || uri?.scheme === 'codespace') { + [repositoryOwner, repositoryName] = uri.authority.split('+'); + } + } + + if (repositoryName && repositoryOwner) { + this.label = localize('openInDesktopLabel', "$(remote) Open in Desktop"); + this.tooltip = localize('openInDesktopTooltip', "Open in Desktop"); + this.command = '_web.openInDesktop'; + this.commandImpl = { + id: this.command, + handler: () => { + const protocol = product.quality === 'stable' ? 'vscode' : 'vscode-insiders'; + window.open(`${protocol}://vscode.git/clone?url=${encodeURIComponent(`https://github.com/${repositoryOwner}/${repositoryName}.git`)}`); + } + }; + } else { + this.label = localize('playgroundLabel', "Web Playground"); + this.tooltip = this.label; + } + } +} + (function () { // Find config by checking for DOM @@ -343,14 +391,28 @@ class WorkspaceProvider implements IWorkspaceProvider { } } + // Home Indicator + const homeIndicator: IHomeIndicator = { + href: 'https://github.com/Microsoft/vscode', + icon: 'code', + title: localize('home', "Home") + }; + + // Commands + const commands: ICommand[] = []; + + // Remote indicator + const remoteIndicator = new RemoteIndicator(workspace); + if (remoteIndicator.commandImpl) { + commands.push(remoteIndicator.commandImpl); + } + // Finally create workbench create(document.body, { ...config, - homeIndicator: { - href: 'https://github.com/Microsoft/vscode', - icon: 'code', - title: localize('home', "Home") - }, + homeIndicator, + commands, + remoteIndicator, workspaceProvider: new WorkspaceProvider(workspace, payload), urlCallbackProvider: new PollingURLCallbackProvider(), credentialsProvider: new LocalStorageCredentialsProvider() diff --git a/src/vs/workbench/browser/contextkeys.ts b/src/vs/workbench/browser/contextkeys.ts index 118699c1986c79feecd0d0fe15a886bdbb5f6a34..1de01f036f34c3072c44e546fc3c846ca86ab2a0 100644 --- a/src/vs/workbench/browser/contextkeys.ts +++ b/src/vs/workbench/browser/contextkeys.ts @@ -21,8 +21,6 @@ import { PanelPositionContext } from 'vs/workbench/common/panel'; import { getRemoteName } from 'vs/platform/remote/common/remoteHosts'; import { IWorkingCopyService } from 'vs/workbench/services/workingCopy/common/workingCopyService'; -export const Deprecated_RemoteAuthorityContext = new RawContextKey('remoteAuthority', ''); - export const RemoteNameContext = new RawContextKey('remoteName', ''); export const RemoteConnectionState = new RawContextKey<'' | 'initializing' | 'disconnected' | 'connected'>('remoteConnectionState', ''); diff --git a/src/vs/workbench/contrib/remote/browser/remote.ts b/src/vs/workbench/contrib/remote/browser/remote.ts index e9a398cc506d9ec35601f3f6471b98eb168ad520..0446e6aa7951fe195590f90e161755b0c330a2ef 100644 --- a/src/vs/workbench/contrib/remote/browser/remote.ts +++ b/src/vs/workbench/contrib/remote/browser/remote.ts @@ -53,7 +53,7 @@ import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { Event } from 'vs/base/common/event'; import { ExtensionsRegistry, IExtensionPointUser } from 'vs/workbench/services/extensions/common/extensionsRegistry'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; -import { RemoteWindowActiveIndicator } from 'vs/workbench/contrib/remote/browser/remoteIndicator'; +import { RemoteStatusIndicator } from 'vs/workbench/contrib/remote/browser/remoteIndicator'; import { inQuickPickContextKeyValue } from 'vs/workbench/browser/quickaccess'; import { Codicon, registerIcon } from 'vs/base/common/codicons'; import { ITerminalService } from 'vs/workbench/contrib/terminal/browser/terminal'; @@ -838,4 +838,4 @@ class RemoteAgentConnectionStatusListener implements IWorkbenchContribution { const workbenchContributionsRegistry = Registry.as(WorkbenchExtensions.Workbench); workbenchContributionsRegistry.registerWorkbenchContribution(RemoteAgentConnectionStatusListener, LifecyclePhase.Eventually); -workbenchContributionsRegistry.registerWorkbenchContribution(RemoteWindowActiveIndicator, LifecyclePhase.Starting); +workbenchContributionsRegistry.registerWorkbenchContribution(RemoteStatusIndicator, LifecyclePhase.Starting); diff --git a/src/vs/workbench/contrib/remote/browser/remoteIndicator.ts b/src/vs/workbench/contrib/remote/browser/remoteIndicator.ts index 7aec94c4f7edd9ac3c8793a78204f5016baaf21e..63cd68da412cb900f8ec9270cc79eaec6ae25c2f 100644 --- a/src/vs/workbench/contrib/remote/browser/remoteIndicator.ts +++ b/src/vs/workbench/contrib/remote/browser/remoteIndicator.ts @@ -7,7 +7,7 @@ import * as nls from 'vs/nls'; import { STATUS_BAR_HOST_NAME_BACKGROUND, STATUS_BAR_HOST_NAME_FOREGROUND } from 'vs/workbench/common/theme'; import { themeColorFromId } from 'vs/platform/theme/common/themeService'; import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; -import { Disposable } from 'vs/base/common/lifecycle'; +import { Disposable, dispose } from 'vs/base/common/lifecycle'; import { MenuId, IMenuService, MenuItemAction, IMenu, MenuRegistry, registerAction2, Action2 } from 'vs/platform/actions/common/actions'; import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; import { StatusbarAlignment, IStatusbarService, IStatusbarEntryAccessor, IStatusbarEntry } from 'vs/workbench/services/statusbar/common/statusbar'; @@ -21,88 +21,112 @@ import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/ import { PersistentConnectionEventType } from 'vs/platform/remote/common/remoteAgentConnection'; import { IRemoteAuthorityResolverService } from 'vs/platform/remote/common/remoteAuthorityResolver'; import { IHostService } from 'vs/workbench/services/host/browser/host'; -import { RemoteConnectionState, Deprecated_RemoteAuthorityContext } from 'vs/workbench/browser/contextkeys'; +import { RemoteConnectionState } from 'vs/workbench/browser/contextkeys'; import { isWeb } from 'vs/base/common/platform'; import { once } from 'vs/base/common/functional'; -const WINDOW_ACTIONS_COMMAND_ID = 'workbench.action.remote.showMenu'; -const CLOSE_REMOTE_COMMAND_ID = 'workbench.action.remote.close'; -const SHOW_CLOSE_REMOTE_COMMAND_ID = !isWeb; // web does not have a "Close Remote" command +export class RemoteStatusIndicator extends Disposable implements IWorkbenchContribution { -export class RemoteWindowActiveIndicator extends Disposable implements IWorkbenchContribution { + private static REMOTE_ACTIONS_COMMAND_ID = 'workbench.action.remote.showMenu'; + private static CLOSE_REMOTE_COMMAND_ID = 'workbench.action.remote.close'; + private static SHOW_CLOSE_REMOTE_COMMAND_ID = !isWeb; // web does not have a "Close Remote" command - private windowIndicatorEntry: IStatusbarEntryAccessor | undefined; - private windowCommandMenu: IMenu; - private hasWindowActions: boolean = false; - private remoteAuthority: string | undefined; + private remoteStatusEntry: IStatusbarEntryAccessor | undefined; + + private remoteMenu = this._register(this.menuService.createMenu(MenuId.StatusBarWindowIndicatorMenu, this.contextKeyService)); + private hasRemoteActions = false; + + private remoteAuthority = this.environmentService.configuration.remoteAuthority; private connectionState: 'initializing' | 'connected' | 'disconnected' | undefined = undefined; + private connectionStateContextKey = RemoteConnectionState.bindTo(this.contextKeyService); constructor( @IStatusbarService private readonly statusbarService: IStatusbarService, - @IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService, + @IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService, @ILabelService private readonly labelService: ILabelService, @IContextKeyService private contextKeyService: IContextKeyService, @IMenuService private menuService: IMenuService, @IQuickInputService private readonly quickInputService: IQuickInputService, @ICommandService private readonly commandService: ICommandService, - @IExtensionService extensionService: IExtensionService, - @IRemoteAgentService remoteAgentService: IRemoteAgentService, - @IRemoteAuthorityResolverService remoteAuthorityResolverService: IRemoteAuthorityResolverService, - @IHostService hostService: IHostService + @IExtensionService private readonly extensionService: IExtensionService, + @IRemoteAgentService private readonly remoteAgentService: IRemoteAgentService, + @IRemoteAuthorityResolverService private readonly remoteAuthorityResolverService: IRemoteAuthorityResolverService, + @IHostService private readonly hostService: IHostService ) { super(); - this.windowCommandMenu = this.menuService.createMenu(MenuId.StatusBarWindowIndicatorMenu, this.contextKeyService); - this._register(this.windowCommandMenu); + // Set initial connection state + if (this.remoteAuthority) { + this.connectionState = 'initializing'; + this.connectionStateContextKey.set(this.connectionState); + } + + this.registerActions(); + this.registerListeners(); + + this.updateWhenInstalledExtensionsRegistered(); + this.updateRemoteStatusIndicator(); + } + private registerActions(): void { const category = { value: nls.localize('remote.category', "Remote"), original: 'Remote' }; + + // Show Remote Menu const that = this; registerAction2(class extends Action2 { constructor() { super({ - id: WINDOW_ACTIONS_COMMAND_ID, + id: RemoteStatusIndicator.REMOTE_ACTIONS_COMMAND_ID, category, title: { value: nls.localize('remote.showMenu', "Show Remote Menu"), original: 'Show Remote Menu' }, f1: true, }); } - run = () => that.showIndicatorActions(that.windowCommandMenu); + run = () => that.showRemoteMenu(that.remoteMenu); }); - this.remoteAuthority = environmentService.configuration.remoteAuthority; - Deprecated_RemoteAuthorityContext.bindTo(this.contextKeyService).set(this.remoteAuthority || ''); + // Close Remote Connection + if (RemoteStatusIndicator.SHOW_CLOSE_REMOTE_COMMAND_ID && this.remoteAuthority) { + registerAction2(class extends Action2 { + constructor() { + super({ + id: RemoteStatusIndicator.CLOSE_REMOTE_COMMAND_ID, + category, + title: { value: nls.localize('remote.close', "Close Remote Connection"), original: 'Close Remote Connection' }, + f1: true + }); + } + run = () => that.remoteAuthority && that.hostService.openWindow({ forceReuseWindow: true }); + }); - if (this.remoteAuthority) { + MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { + group: '6_close', + command: { + id: RemoteStatusIndicator.CLOSE_REMOTE_COMMAND_ID, + title: nls.localize({ key: 'miCloseRemote', comment: ['&& denotes a mnemonic'] }, "Close Re&&mote Connection") + }, + order: 3.5 + }); + } + } - if (SHOW_CLOSE_REMOTE_COMMAND_ID) { - registerAction2(class extends Action2 { - constructor() { - super({ - id: CLOSE_REMOTE_COMMAND_ID, - category, - title: { value: nls.localize('remote.close', "Close Remote Connection"), original: 'Close Remote Connection' }, - f1: true - }); - } - run = () => that.remoteAuthority && hostService.openWindow({ forceReuseWindow: true }); - }); + private registerListeners(): void { - MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { - group: '6_close', - command: { - id: CLOSE_REMOTE_COMMAND_ID, - title: nls.localize({ key: 'miCloseRemote', comment: ['&& denotes a mnemonic'] }, "Close Re&&mote Connection") - }, - order: 3.5 - }); - } + // Menu changes + this._register(this.remoteMenu.onDidChange(() => this.updateRemoteActions())); - // Pending entry until extensions are ready - this.renderWindowIndicator('$(sync~spin) ' + nls.localize('host.open', "Opening Remote..."), undefined, WINDOW_ACTIONS_COMMAND_ID); - this.connectionState = 'initializing'; - RemoteConnectionState.bindTo(this.contextKeyService).set(this.connectionState); + // Update indicator when formatter changes as it may have an impact on the remote label + this._register(this.labelService.onDidChangeFormatters(() => this.updateRemoteStatusIndicator())); - const connection = remoteAgentService.getConnection(); + // Update based on remote indicator changes if any + const remoteIndicator = this.environmentService.options?.remoteIndicator; + if (remoteIndicator) { + this._register(remoteIndicator.onDidChange(() => this.updateRemoteStatusIndicator())); + } + + // Listen to changes of the connection + if (this.remoteAuthority) { + const connection = this.remoteAgentService.getConnection(); if (connection) { this._register(connection.onDidStateChange((e) => { switch (e.type) { @@ -119,72 +143,106 @@ export class RemoteWindowActiveIndicator extends Disposable implements IWorkbenc })); } } + } - extensionService.whenInstalledExtensionsRegistered().then(_ => { - if (this.remoteAuthority) { - this._register(this.labelService.onDidChangeFormatters(e => this.updateWindowIndicator())); - remoteAuthorityResolverService.resolveAuthority(this.remoteAuthority).then(() => this.setDisconnected(false), () => this.setDisconnected(true)); - } - this._register(this.windowCommandMenu.onDidChange(e => this.updateWindowActions())); - this.updateWindowIndicator(); - }); + private async updateWhenInstalledExtensionsRegistered(): Promise { + await this.extensionService.whenInstalledExtensionsRegistered(); + + const remoteAuthority = this.remoteAuthority; + if (remoteAuthority) { + + // Try to resolve the authority to figure out connection state + (async () => { + try { + await this.remoteAuthorityResolverService.resolveAuthority(remoteAuthority); + + this.setDisconnected(false); + } catch (error) { + this.setDisconnected(true); + } + })(); + } + + this.updateRemoteStatusIndicator(); } private setDisconnected(isDisconnected: boolean): void { const newState = isDisconnected ? 'disconnected' : 'connected'; if (this.connectionState !== newState) { this.connectionState = newState; - RemoteConnectionState.bindTo(this.contextKeyService).set(this.connectionState); - Deprecated_RemoteAuthorityContext.bindTo(this.contextKeyService).set(isDisconnected ? `disconnected/${this.remoteAuthority!}` : this.remoteAuthority!); - this.updateWindowIndicator(); + this.connectionStateContextKey.set(this.connectionState); + + this.updateRemoteStatusIndicator(); } } - private updateWindowIndicator(): void { - const windowActionCommand = (this.remoteAuthority || this.windowCommandMenu.getActions().length) ? WINDOW_ACTIONS_COMMAND_ID : undefined; - if (this.remoteAuthority) { + private updateRemoteActions() { + const newHasWindowActions = this.remoteMenu.getActions().length > 0; + if (newHasWindowActions !== this.hasRemoteActions) { + this.hasRemoteActions = newHasWindowActions; + + this.updateRemoteStatusIndicator(); + } + } + + private updateRemoteStatusIndicator(): void { + + // Remote indicator: show if provided via options + const remoteIndicator = this.environmentService.options?.remoteIndicator; + if (remoteIndicator) { + this.renderRemoteStatusIndicator(remoteIndicator.label, remoteIndicator.tooltip, remoteIndicator.command); + } + + // Remote Authority: show connection state + else if (this.remoteAuthority) { const hostLabel = this.labelService.getHostLabel(REMOTE_HOST_SCHEME, this.remoteAuthority) || this.remoteAuthority; - if (this.connectionState !== 'disconnected') { - this.renderWindowIndicator(`$(remote) ${hostLabel}`, nls.localize('host.tooltip', "Editing on {0}", hostLabel), windowActionCommand); - } else { - this.renderWindowIndicator(`$(alert) ${nls.localize('disconnectedFrom', "Disconnected from")} ${hostLabel}`, nls.localize('host.tooltipDisconnected', "Disconnected from {0}", hostLabel), windowActionCommand); - } - } else { - if (windowActionCommand) { - this.renderWindowIndicator(`$(remote)`, nls.localize('noHost.tooltip', "Open a remote window"), windowActionCommand); - } else if (this.windowIndicatorEntry) { - this.windowIndicatorEntry.dispose(); - this.windowIndicatorEntry = undefined; + switch (this.connectionState) { + case 'initializing': + this.renderRemoteStatusIndicator(`$(sync~spin) ${nls.localize('host.open', "Opening Remote...")}`, nls.localize('host.open', "Opening Remote...")); + break; + case 'disconnected': + this.renderRemoteStatusIndicator(`$(alert) ${nls.localize('disconnectedFrom', "Disconnected from {0}", hostLabel)}`, nls.localize('host.tooltipDisconnected', "Disconnected from {0}", hostLabel)); + break; + default: + this.renderRemoteStatusIndicator(`$(remote) ${hostLabel}`, nls.localize('host.tooltip', "Editing on {0}", hostLabel)); } } - } - private updateWindowActions() { - const newHasWindowActions = this.windowCommandMenu.getActions().length > 0; - if (newHasWindowActions !== this.hasWindowActions) { - this.hasWindowActions = newHasWindowActions; - this.updateWindowIndicator(); + // Remote Extensions Installed: offer the indicator to show actions + else if (this.remoteMenu.getActions().length > 0) { + this.renderRemoteStatusIndicator(`$(remote)`, nls.localize('noHost.tooltip', "Open a Remote Window")); + } + + // No Remote Extensions: hide status indicator + else { + dispose(this.remoteStatusEntry); + this.remoteStatusEntry = undefined; } } - private renderWindowIndicator(text: string, tooltip?: string, command?: string): void { + private renderRemoteStatusIndicator(text: string, tooltip?: string, command?: string): void { + const name = nls.localize('remoteHost', "Remote Host"); + if (typeof command !== 'string' && this.remoteMenu.getActions().length > 0) { + command = RemoteStatusIndicator.REMOTE_ACTIONS_COMMAND_ID; + } + const properties: IStatusbarEntry = { backgroundColor: themeColorFromId(STATUS_BAR_HOST_NAME_BACKGROUND), color: themeColorFromId(STATUS_BAR_HOST_NAME_FOREGROUND), - ariaLabel: nls.localize('remote', "Remote"), + ariaLabel: name, text, tooltip, command }; - if (this.windowIndicatorEntry) { - this.windowIndicatorEntry.update(properties); + + if (this.remoteStatusEntry) { + this.remoteStatusEntry.update(properties); } else { - this.windowIndicatorEntry = this.statusbarService.addEntry(properties, 'status.host', nls.localize('status.host', "Remote Host"), StatusbarAlignment.LEFT, Number.MAX_VALUE /* first entry */); + this.remoteStatusEntry = this.statusbarService.addEntry(properties, 'status.host', name, StatusbarAlignment.LEFT, Number.MAX_VALUE /* first entry */); } } - private showIndicatorActions(menu: IMenu) { - + private showRemoteMenu(menu: IMenu) { const actions = menu.getActions(); const items: (IQuickPickItem | IQuickPickSeparator)[] = []; @@ -192,6 +250,7 @@ export class RemoteWindowActiveIndicator extends Disposable implements IWorkbenc if (items.length) { items.push({ type: 'separator' }); } + for (let action of actionGroup[1]) { if (action instanceof MenuItemAction) { let label = typeof action.item.title === 'string' ? action.item.title : action.item.title.value; @@ -199,6 +258,7 @@ export class RemoteWindowActiveIndicator extends Disposable implements IWorkbenc const category = typeof action.item.category === 'string' ? action.item.category : action.item.category.value; label = nls.localize('cat.title', "{0}: {1}", category, label); } + items.push({ type: 'item', id: action.item.id, @@ -208,13 +268,14 @@ export class RemoteWindowActiveIndicator extends Disposable implements IWorkbenc } } - if (SHOW_CLOSE_REMOTE_COMMAND_ID && this.remoteAuthority) { + if (RemoteStatusIndicator.SHOW_CLOSE_REMOTE_COMMAND_ID && this.remoteAuthority) { if (items.length) { items.push({ type: 'separator' }); } + items.push({ type: 'item', - id: CLOSE_REMOTE_COMMAND_ID, + id: RemoteStatusIndicator.CLOSE_REMOTE_COMMAND_ID, label: nls.localize('closeRemote.title', 'Close Remote Connection') }); } @@ -227,8 +288,10 @@ export class RemoteWindowActiveIndicator extends Disposable implements IWorkbenc if (selectedItems.length === 1) { this.commandService.executeCommand(selectedItems[0].id!); } + quickPick.hide(); })); + quickPick.show(); } } diff --git a/src/vs/workbench/workbench.web.api.ts b/src/vs/workbench/workbench.web.api.ts index 2bd2d89baea80f047d6c1a4c476931bad179e65e..9331e328ea3ffc4c00b49ded9e5d133bb2a15ae1 100644 --- a/src/vs/workbench/workbench.web.api.ts +++ b/src/vs/workbench/workbench.web.api.ts @@ -121,6 +121,32 @@ interface IHomeIndicator { title: string; } +interface IRemoteIndicator { + + /** + * Triggering this event will cause the remote indicator to update. + */ + onDidChange: Event; + + /** + * Label of the remote indicator may include octicons + * e.g. `$(remote) label` + */ + label: string; + + /** + * Tooltip of the remote indicator should not include + * octicons and be descriptive. + */ + tooltip: string; + + /** + * If provided, overrides the default command that + * is executed when clicking on the remote indicator. + */ + command?: string; +} + interface IDefaultSideBarLayout { visible?: boolean; containers?: ({ @@ -204,6 +230,11 @@ interface IWorkbenchConstructionOptions { */ readonly connectionToken?: string; + /** + * Session id of the current authenticated user + */ + readonly authenticationSessionId?: string; + /** * An endpoint to serve iframe content ("webview") from. This is required * to provide full security isolation from the workbench host. @@ -231,6 +262,11 @@ interface IWorkbenchConstructionOptions { */ readonly tunnelProvider?: ITunnelProvider; + /** + * Endpoints to be used for proxying authentication code exchange calls in the browser. + */ + readonly codeExchangeProxyEndpoints?: { [providerId: string]: string } + //#endregion @@ -247,11 +283,6 @@ interface IWorkbenchConstructionOptions { */ userDataProvider?: IFileSystemProvider; - /** - * Session id of the current authenticated user - */ - readonly authenticationSessionId?: string; - /** * Enables user data sync by default and syncs into the current authenticated user account using the provided [authenticationSessionId}(#authenticationSessionId). */ @@ -345,6 +376,11 @@ interface IWorkbenchConstructionOptions { */ readonly productConfiguration?: Partial; + /** + * Optional override for properties of the remote window indicator in the status bar. + */ + readonly remoteIndicator?: IRemoteIndicator; + //#endregion @@ -360,11 +396,6 @@ interface IWorkbenchConstructionOptions { */ readonly driver?: boolean; - /** - * Endpoints to be used for proxying authentication code exchange calls in the browser. - */ - readonly codeExchangeProxyEndpoints?: { [providerId: string]: string } - //#endregion } @@ -504,6 +535,7 @@ export { // Branding IHomeIndicator, IProductConfiguration, + IRemoteIndicator, // Default layout IDefaultView,