diff --git a/build/gulpfile.vscode.ts b/build/gulpfile.vscode.ts index ac122c3794028a..43cf08bbaa3ef4 100644 --- a/build/gulpfile.vscode.ts +++ b/build/gulpfile.vscode.ts @@ -476,6 +476,7 @@ function packageTask(platform: string, arch: string, sourceFolderName: string, d 'resources/win32/react.ico', 'resources/win32/ruby.ico', 'resources/win32/sass.ico', + 'resources/win32/sessions.ico', 'resources/win32/shell.ico', 'resources/win32/sql.ico', 'resources/win32/typescript.ico', diff --git a/resources/win32/sessions.ico b/resources/win32/sessions.ico new file mode 100644 index 00000000000000..791ed72afc7bcc Binary files /dev/null and b/resources/win32/sessions.ico differ diff --git a/src/vs/platform/native/electron-main/nativeHostMainService.ts b/src/vs/platform/native/electron-main/nativeHostMainService.ts index b1fb89e25a3acb..8ce5c511ce030f 100644 --- a/src/vs/platform/native/electron-main/nativeHostMainService.ts +++ b/src/vs/platform/native/electron-main/nativeHostMainService.ts @@ -219,7 +219,8 @@ export class NativeHostMainService extends Disposable implements INativeHostMain workspace: window.openedWorkspace ?? toWorkspaceIdentifier(window.backupPath, window.isExtensionDevelopmentHost), title: window.win?.getTitle() ?? '', filename: window.getRepresentedFilename(), - dirty: window.isDocumentEdited() + dirty: window.isDocumentEdited(), + iconPath: window.iconPath })); const auxiliaryWindows = []; diff --git a/src/vs/platform/window/common/window.ts b/src/vs/platform/window/common/window.ts index 357bf62d065e64..d2dc38e615d081 100644 --- a/src/vs/platform/window/common/window.ts +++ b/src/vs/platform/window/common/window.ts @@ -92,6 +92,7 @@ interface IOpenedWindow { export interface IOpenedMainWindow extends IOpenedWindow { readonly workspace?: IAnyWorkspaceIdentifier; readonly dirty: boolean; + readonly iconPath?: URI; } export interface IOpenedAuxiliaryWindow extends IOpenedWindow { diff --git a/src/vs/platform/window/electron-main/window.ts b/src/vs/platform/window/electron-main/window.ts index fe369ee4da2cc4..ccf4b58c522bad 100644 --- a/src/vs/platform/window/electron-main/window.ts +++ b/src/vs/platform/window/electron-main/window.ts @@ -7,6 +7,7 @@ import electron from 'electron'; import { CancellationToken } from '../../../base/common/cancellation.js'; import { Event } from '../../../base/common/event.js'; import { IDisposable } from '../../../base/common/lifecycle.js'; +import { URI } from '../../../base/common/uri.js'; import { ISerializableCommandAction } from '../../action/common/action.js'; import { NativeParsedArgs } from '../../environment/common/argv.js'; import { FocusMode, IApplicationBadge } from '../../native/common/native.js'; @@ -63,6 +64,8 @@ export interface ICodeWindow extends IBaseWindow { readonly remoteAuthority?: string; + readonly iconPath?: URI; + readonly isExtensionDevelopmentHost: boolean; readonly isExtensionTestHost: boolean; diff --git a/src/vs/platform/windows/electron-main/windowImpl.ts b/src/vs/platform/windows/electron-main/windowImpl.ts index 5904f68759d693..61bd6c7dcafa62 100644 --- a/src/vs/platform/windows/electron-main/windowImpl.ts +++ b/src/vs/platform/windows/electron-main/windowImpl.ts @@ -10,6 +10,7 @@ import { toErrorMessage } from '../../../base/common/errorMessage.js'; import { Emitter, Event } from '../../../base/common/event.js'; import { Disposable, DisposableStore, IDisposable, MutableDisposable, toDisposable } from '../../../base/common/lifecycle.js'; import { FileAccess, Schemas } from '../../../base/common/network.js'; +import { join } from '../../../base/common/path.js'; import { getMarks, mark } from '../../../base/common/performance.js'; import { isTahoeOrNewer, isLinux, isMacintosh, isWindows } from '../../../base/common/platform.js'; import { URI } from '../../../base/common/uri.js'; @@ -701,6 +702,9 @@ export class CodeWindow extends BaseWindow implements ICodeWindow { get remoteAuthority(): string | undefined { return this._config?.remoteAuthority; } + private readonly _iconPath: URI | undefined; + get iconPath(): URI | undefined { return this._iconPath; } + private _config: INativeWindowConfiguration | undefined; get config(): INativeWindowConfiguration | undefined { return this._config; } @@ -777,6 +781,11 @@ export class CodeWindow extends BaseWindow implements ICodeWindow { } const options = instantiationService.invokeFunction(defaultBrowserWindowOptions, this.windowState, undefined, webPreferences); + const iconPath = config.isSessionsWindow && isWindows ? join(this.environmentMainService.appRoot, 'resources/win32/sessions.ico') : undefined; + if (iconPath) { + options.icon = iconPath; + } + this._iconPath = iconPath ? URI.file(iconPath) : undefined; // Create the browser window mark('code/willCreateCodeBrowserWindow'); diff --git a/src/vs/platform/windows/test/electron-main/windowsFinder.test.ts b/src/vs/platform/windows/test/electron-main/windowsFinder.test.ts index 85214ff775b894..476aa933a16abe 100644 --- a/src/vs/platform/windows/test/electron-main/windowsFinder.test.ts +++ b/src/vs/platform/windows/test/electron-main/windowsFinder.test.ts @@ -50,6 +50,7 @@ suite('WindowsFinder', () => { openedWorkspace = options.openedFolderUri ? { id: '', uri: options.openedFolderUri } : options.openedWorkspace; backupPath?: string | undefined; remoteAuthority?: string | undefined; + iconPath?: URI | undefined; isExtensionDevelopmentHost = false; isExtensionTestHost = false; lastFocusTime = options.lastFocusTime; diff --git a/src/vs/platform/workspaces/electron-main/workspacesHistoryMainService.ts b/src/vs/platform/workspaces/electron-main/workspacesHistoryMainService.ts index 70356b907ff0bf..2339704fcb150e 100644 --- a/src/vs/platform/workspaces/electron-main/workspacesHistoryMainService.ts +++ b/src/vs/platform/workspaces/electron-main/workspacesHistoryMainService.ts @@ -10,6 +10,7 @@ import { Emitter, Event as CommonEvent } from '../../../base/common/event.js'; import { normalizeDriveLetter, splitRecentLabel } from '../../../base/common/labels.js'; import { Disposable } from '../../../base/common/lifecycle.js'; import { Schemas } from '../../../base/common/network.js'; +import { join } from '../../../base/common/path.js'; import { isMacintosh, isWindows } from '../../../base/common/platform.js'; import { basename, dirname, extUriBiasedIgnorePathCase, isEqual, originalFSPath } from '../../../base/common/resources.js'; import { URI } from '../../../base/common/uri.js'; @@ -372,6 +373,7 @@ export class WorkspacesHistoryMainService extends Disposable implements IWorkspa } const jumpList: JumpListCategory[] = []; + let recentWorkspaces = this.getWindowsJumpListWorkspaces((await this.getRecentlyOpened()).workspaces); // Tasks jumpList.push({ @@ -385,12 +387,21 @@ export class WorkspacesHistoryMainService extends Disposable implements IWorkspa args: '-n', // force new window iconPath: process.execPath, iconIndex: 0 + }, + { + type: 'task', + title: localize('agentsWindow', "Agents Window"), + description: localize('openAgentsWindowDesc', "Opens the Agents Window"), + program: process.execPath, + args: '--agents', + iconPath: join(this.environmentMainService.appRoot, 'resources/win32/sessions.ico'), + iconIndex: 0 } ] }); // Recent Workspaces - if ((await this.getRecentlyOpened()).workspaces.length > 0) { + if (recentWorkspaces.length > 0) { // The user might have meanwhile removed items from the jump list and we have to respect that // so we need to update our list of recent paths with the choice of the user to not add them again @@ -408,10 +419,11 @@ export class WorkspacesHistoryMainService extends Disposable implements IWorkspa } } await this.removeRecentlyOpened(toRemove); + recentWorkspaces = this.getWindowsJumpListWorkspaces((await this.getRecentlyOpened()).workspaces); // Add entries up to the slot count Explorer requested (jumpListSettings.minItems). let hasWorkspaces = false; - const items: JumpListItem[] = coalesce((await this.getRecentlyOpened()).workspaces.slice(0, jumpListSettings.minItems).map(recent => { + const items: JumpListItem[] = coalesce(recentWorkspaces.slice(0, jumpListSettings.minItems).map(recent => { const workspace = isRecentWorkspace(recent) ? recent.workspace : recent.folderUri; const { title, description } = this.getWindowsJumpListLabel(workspace, recent.label); @@ -458,6 +470,10 @@ export class WorkspacesHistoryMainService extends Disposable implements IWorkspa } } + private getWindowsJumpListWorkspaces(workspaces: Array): Array { + return workspaces.filter(recent => isRecentFolder(recent) || !this.isAgentSessionsWorkspace(recent.workspace)); + } + private getWindowsJumpListLabel(workspace: IWorkspaceIdentifier | URI, recentLabel: string | undefined): { title: string; description: string } { // Prefer recent label diff --git a/src/vs/workbench/electron-browser/actions/windowActions.ts b/src/vs/workbench/electron-browser/actions/windowActions.ts index 7cabd5de204f16..e2659d27dbc997 100644 --- a/src/vs/workbench/electron-browser/actions/windowActions.ts +++ b/src/vs/workbench/electron-browser/actions/windowActions.ts @@ -300,7 +300,8 @@ abstract class BaseSwitchWindow extends Action2 { windowId: window.id, label: window.title, ariaLabel: window.dirty ? localize('windowDirtyAriaLabel', "{0}, window with unsaved changes", window.title) : window.title, - iconClasses: getIconClasses(modelService, languageService, resource, fileKind), + iconPath: window.iconPath ? { dark: window.iconPath } : undefined, + iconClasses: window.iconPath ? undefined : getIconClasses(modelService, languageService, resource, fileKind), description: (currentWindowId === window.id) ? localize('current', "Current Window") : undefined, buttons: window.dirty ? [this.closeDirtyWindowAction] : currentWindowId === window.id ? [this.closeActiveWindowAction] : [this.closeWindowAction] };