From acfadf071051efd941bea16550a8baf7b2e7e422 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Wed, 11 Mar 2026 14:39:31 +0100 Subject: [PATCH 1/5] Sessions: reorder pickers, rename isolation labels, gate on config - Swap mode and model picker positions in toolbar - Move Default Approvals next to isolation mode picker with gap - Rename Folder/Worktree labels to Local/Copilot CLI - Gate isolation picker on github.copilot.chat.cli.isolationOption.enabled - Show picker as disabled (not hidden) when config is off - Add setEnabled API to IsolationModePicker - Listen for config changes and enforce worktree mode when disabled Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../contrib/chat/browser/newChatViewPane.ts | 59 ++++++++++++++++--- .../chat/browser/sessionTargetPicker.ts | 40 +++++++++---- 2 files changed, 81 insertions(+), 18 deletions(-) diff --git a/src/vs/sessions/contrib/chat/browser/newChatViewPane.ts b/src/vs/sessions/contrib/chat/browser/newChatViewPane.ts index 09a3bc8132840..79f09f1830c56 100644 --- a/src/vs/sessions/contrib/chat/browser/newChatViewPane.ts +++ b/src/vs/sessions/contrib/chat/browser/newChatViewPane.ts @@ -132,6 +132,7 @@ class NewChatWidget extends Disposable implements IHistoryNavigationWidget { // Send button private _sendButton: Button | undefined; private _sending = false; + private _altKeyDown = false; // Repository loading private readonly _openRepositoryCts = this._register(new MutableDisposable()); @@ -211,7 +212,7 @@ class NewChatWidget extends Disposable implements IHistoryNavigationWidget { this._register(this._targetPicker.onDidChangeTarget((target) => { this._createNewSession(); const isLocal = target === AgentSessionProviders.Background; - this._isolationModePicker.setVisible(isLocal); + this._updateIsolationPickerVisibility(); this._permissionPicker.setVisible(isLocal); this._branchPicker.setVisible(isLocal); this._syncIndicator.setVisible(isLocal); @@ -275,6 +276,27 @@ class NewChatWidget extends Disposable implements IHistoryNavigationWidget { this._currentLanguageModel.read(reader); this._updateDraftState(); })); + + // When isolation option config changes, update picker visibility + this._register(this.configurationService.onDidChangeConfiguration(e => { + if (e.affectsConfiguration('github.copilot.chat.cli.isolationOption.enabled')) { + this._updateIsolationPickerVisibility(); + } + })); + } + + private get _isIsolationOptionEnabled(): boolean { + return this.configurationService.getValue('github.copilot.chat.cli.isolationOption.enabled') !== false; + } + + private _updateIsolationPickerVisibility(): void { + const isLocal = this._targetPicker.selectedTarget === AgentSessionProviders.Background; + const enabled = this._isIsolationOptionEnabled; + if (!enabled) { + this._isolationModePicker.setIsolationMode('worktree'); + } + this._isolationModePicker.setVisible(isLocal); + this._isolationModePicker.setEnabled(enabled); } // --- Rendering --- @@ -324,9 +346,9 @@ class NewChatWidget extends Disposable implements IHistoryNavigationWidget { // Set initial visibility based on default target and isolation mode const isLocal = this._targetPicker.selectedTarget === AgentSessionProviders.Background; - const isWorktree = this._isolationModePicker.isolationMode === 'worktree'; - this._isolationModePicker.setVisible(isLocal); + this._updateIsolationPickerVisibility(); this._permissionPicker.setVisible(isLocal); + const isWorktree = this._isolationModePicker.isolationMode === 'worktree'; this._branchPicker.setVisible(isLocal && isWorktree); this._syncIndicator.setVisible(isLocal && isWorktree); @@ -443,6 +465,7 @@ class NewChatWidget extends Disposable implements IHistoryNavigationWidget { this._updateInputLoadingState(); this._branchPicker.setRepository(undefined); this._isolationModePicker.setRepository(undefined); + this._updateIsolationPickerVisibility(); this._syncIndicator.setRepository(undefined); this._modePicker.setRepository(undefined); @@ -453,6 +476,7 @@ class NewChatWidget extends Disposable implements IHistoryNavigationWidget { this._repositoryLoading = false; this._updateInputLoadingState(); this._isolationModePicker.setRepository(repository); + this._updateIsolationPickerVisibility(); this._branchPicker.setRepository(repository); this._syncIndicator.setRepository(repository); this._modePicker.setRepository(repository); @@ -464,6 +488,7 @@ class NewChatWidget extends Disposable implements IHistoryNavigationWidget { this._repositoryLoading = false; this._updateInputLoadingState(); this._isolationModePicker.setRepository(undefined); + this._updateIsolationPickerVisibility(); this._branchPicker.setRepository(undefined); this._syncIndicator.setRepository(undefined); this._modePicker.setRepository(undefined); @@ -561,7 +586,7 @@ class NewChatWidget extends Disposable implements IHistoryNavigationWidget { if (e.keyCode === KeyCode.Enter && !e.shiftKey && !e.ctrlKey && e.altKey) { e.preventDefault(); e.stopPropagation(); - this._send(); + this._send({ openNewAfterSend: true }); } })); @@ -682,7 +707,19 @@ class NewChatWidget extends Disposable implements IHistoryNavigationWidget { ariaLabel: localize('send', "Send"), })); sendButton.icon = Codicon.send; - this._register(sendButton.onDidClick(() => this._send())); + this._register(sendButton.onDidClick(() => this._send({ openNewAfterSend: this._altKeyDown }))); + this._register(dom.addDisposableListener(dom.getWindow(container), dom.EventType.KEY_DOWN, e => { + if (e.key === 'Alt') { + this._altKeyDown = true; + sendButton.icon = Codicon.runAbove; + } + })); + this._register(dom.addDisposableListener(dom.getWindow(container), dom.EventType.KEY_UP, e => { + if (e.key === 'Alt') { + this._altKeyDown = false; + sendButton.icon = Codicon.send; + } + })); this._updateSendButtonState(); } @@ -995,7 +1032,7 @@ class NewChatWidget extends Disposable implements IHistoryNavigationWidget { this._sendButton.enabled = !this._sending && hasText && !(this._newSession.value?.disabled ?? true); } - private async _send(): Promise { + private async _send(options?: { openNewAfterSend?: boolean }): Promise { let query = this._editor.getModel()?.getValue().trim(); const session = this._newSession.value; if (!query || !session || this._sending) { @@ -1042,6 +1079,7 @@ class NewChatWidget extends Disposable implements IHistoryNavigationWidget { await this.sessionsManagementService.sendRequestForNewSession( session.resource, { + ...options?.openNewAfterSend ? { openNewSessionView: true } : {}, permissionLevel: this._permissionPicker.permissionLevel, } ); @@ -1119,8 +1157,13 @@ class NewChatWidget extends Disposable implements IHistoryNavigationWidget { } } if (draft.isolationMode) { - this._isolationModePicker.setPreferredIsolationMode(draft.isolationMode); - this._isolationModePicker.setIsolationMode(draft.isolationMode); + if (this._isIsolationOptionEnabled) { + this._isolationModePicker.setPreferredIsolationMode(draft.isolationMode); + this._isolationModePicker.setIsolationMode(draft.isolationMode); + } else { + this._isolationModePicker.setPreferredIsolationMode('worktree'); + this._isolationModePicker.setIsolationMode('worktree'); + } } if (draft.branch) { this._branchPicker.setPreferredBranch(draft.branch); diff --git a/src/vs/sessions/contrib/chat/browser/sessionTargetPicker.ts b/src/vs/sessions/contrib/chat/browser/sessionTargetPicker.ts index 68d175b8fbe6c..8112cee73752d 100644 --- a/src/vs/sessions/contrib/chat/browser/sessionTargetPicker.ts +++ b/src/vs/sessions/contrib/chat/browser/sessionTargetPicker.ts @@ -138,6 +138,7 @@ export class IsolationModePicker extends Disposable { private _isolationMode: IsolationMode = 'worktree'; private _preferredIsolationMode: IsolationMode | undefined; private _repository: IGitRepository | undefined; + private _enabled: boolean = true; private readonly _onDidChange = this._register(new Emitter()); readonly onDidChange: Event = this._onDidChange.event; @@ -225,23 +226,38 @@ export class IsolationModePicker extends Disposable { } } + /** + * Enables or disables the picker. When disabled, the picker is shown + * but cannot be interacted with. + */ + setEnabled(enabled: boolean): void { + this._enabled = enabled; + if (this._slotElement) { + this._slotElement.classList.toggle('disabled', !enabled); + } + if (this._triggerElement) { + this._triggerElement.tabIndex = enabled ? 0 : -1; + this._triggerElement.setAttribute('aria-disabled', String(!enabled)); + } + } + private _showPicker(): void { - if (!this._triggerElement || this.actionWidgetService.isVisible || !this._repository) { + if (!this._triggerElement || this.actionWidgetService.isVisible || !this._repository || !this._enabled) { return; } const items: IActionListItem[] = [ { kind: ActionListItemKind.Action, - label: localize('isolationMode.folder', "Folder"), - group: { title: '', icon: Codicon.folder }, - item: 'workspace', + label: localize('isolationMode.worktree', "Copilot CLI"), + group: { title: '', icon: Codicon.worktree }, + item: 'worktree', }, { kind: ActionListItemKind.Action, - label: localize('isolationMode.worktree', "Worktree"), - group: { title: '', icon: Codicon.worktree }, - item: 'worktree', + label: localize('isolationMode.folder', "Local"), + group: { title: '', icon: Codicon.folder }, + item: 'workspace', }, ]; @@ -286,15 +302,19 @@ export class IsolationModePicker extends Disposable { const isDisabled = !this._repository; const modeIcon = this._isolationMode === 'worktree' ? Codicon.worktree : Codicon.folder; const modeLabel = this._isolationMode === 'worktree' - ? localize('isolationMode.worktree', "Worktree") - : localize('isolationMode.folder', "Folder"); + ? localize('isolationMode.worktree', "Copilot CLI") + : localize('isolationMode.folder', "Local"); dom.append(this._triggerElement, renderIcon(modeIcon)); const labelSpan = dom.append(this._triggerElement, dom.$('span.sessions-chat-dropdown-label')); labelSpan.textContent = modeLabel; dom.append(this._triggerElement, renderIcon(Codicon.chevronDown)); - this._slotElement?.classList.toggle('disabled', isDisabled); + this._slotElement?.classList.toggle('disabled', isDisabled || !this._enabled); + if (this._triggerElement) { + this._triggerElement.tabIndex = (!isDisabled && this._enabled) ? 0 : -1; + this._triggerElement.setAttribute('aria-disabled', String(isDisabled || !this._enabled)); + } } } From 7d604ab02defb98adf06e74a84265dcb95e83af9 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Wed, 11 Mar 2026 14:49:24 +0100 Subject: [PATCH 2/5] revert --- .../contrib/chat/browser/newChatViewPane.ts | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/src/vs/sessions/contrib/chat/browser/newChatViewPane.ts b/src/vs/sessions/contrib/chat/browser/newChatViewPane.ts index 79f09f1830c56..386efd14fc119 100644 --- a/src/vs/sessions/contrib/chat/browser/newChatViewPane.ts +++ b/src/vs/sessions/contrib/chat/browser/newChatViewPane.ts @@ -132,7 +132,6 @@ class NewChatWidget extends Disposable implements IHistoryNavigationWidget { // Send button private _sendButton: Button | undefined; private _sending = false; - private _altKeyDown = false; // Repository loading private readonly _openRepositoryCts = this._register(new MutableDisposable()); @@ -707,19 +706,7 @@ class NewChatWidget extends Disposable implements IHistoryNavigationWidget { ariaLabel: localize('send', "Send"), })); sendButton.icon = Codicon.send; - this._register(sendButton.onDidClick(() => this._send({ openNewAfterSend: this._altKeyDown }))); - this._register(dom.addDisposableListener(dom.getWindow(container), dom.EventType.KEY_DOWN, e => { - if (e.key === 'Alt') { - this._altKeyDown = true; - sendButton.icon = Codicon.runAbove; - } - })); - this._register(dom.addDisposableListener(dom.getWindow(container), dom.EventType.KEY_UP, e => { - if (e.key === 'Alt') { - this._altKeyDown = false; - sendButton.icon = Codicon.send; - } - })); + this._register(sendButton.onDidClick(() => this._send())); this._updateSendButtonState(); } From d83f3a39a2d839f900accf1fdf8af85e81a716e2 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Wed, 11 Mar 2026 15:05:34 +0100 Subject: [PATCH 3/5] Update src/vs/sessions/contrib/chat/browser/newChatViewPane.ts Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/vs/sessions/contrib/chat/browser/newChatViewPane.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/sessions/contrib/chat/browser/newChatViewPane.ts b/src/vs/sessions/contrib/chat/browser/newChatViewPane.ts index 386efd14fc119..e7b2c5a86e3e3 100644 --- a/src/vs/sessions/contrib/chat/browser/newChatViewPane.ts +++ b/src/vs/sessions/contrib/chat/browser/newChatViewPane.ts @@ -292,7 +292,7 @@ class NewChatWidget extends Disposable implements IHistoryNavigationWidget { const isLocal = this._targetPicker.selectedTarget === AgentSessionProviders.Background; const enabled = this._isIsolationOptionEnabled; if (!enabled) { - this._isolationModePicker.setIsolationMode('worktree'); + this._isolationModePicker.setPreferredIsolationMode('worktree'); } this._isolationModePicker.setVisible(isLocal); this._isolationModePicker.setEnabled(enabled); From 2c67862345e0c377312a95ad596633b5cf10b9ec Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Wed, 11 Mar 2026 15:05:12 +0100 Subject: [PATCH 4/5] fix compilation --- src/vs/sessions/contrib/chat/browser/newChatViewPane.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/vs/sessions/contrib/chat/browser/newChatViewPane.ts b/src/vs/sessions/contrib/chat/browser/newChatViewPane.ts index e7b2c5a86e3e3..14021e57d0af5 100644 --- a/src/vs/sessions/contrib/chat/browser/newChatViewPane.ts +++ b/src/vs/sessions/contrib/chat/browser/newChatViewPane.ts @@ -585,7 +585,7 @@ class NewChatWidget extends Disposable implements IHistoryNavigationWidget { if (e.keyCode === KeyCode.Enter && !e.shiftKey && !e.ctrlKey && e.altKey) { e.preventDefault(); e.stopPropagation(); - this._send({ openNewAfterSend: true }); + this._send(); } })); @@ -1019,7 +1019,7 @@ class NewChatWidget extends Disposable implements IHistoryNavigationWidget { this._sendButton.enabled = !this._sending && hasText && !(this._newSession.value?.disabled ?? true); } - private async _send(options?: { openNewAfterSend?: boolean }): Promise { + private async _send(): Promise { let query = this._editor.getModel()?.getValue().trim(); const session = this._newSession.value; if (!query || !session || this._sending) { @@ -1066,7 +1066,6 @@ class NewChatWidget extends Disposable implements IHistoryNavigationWidget { await this.sessionsManagementService.sendRequestForNewSession( session.resource, { - ...options?.openNewAfterSend ? { openNewSessionView: true } : {}, permissionLevel: this._permissionPicker.permissionLevel, } ); From 8209d8419c4a61cf5c4431e8a2c292c50cc82f5b Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Wed, 11 Mar 2026 15:07:29 +0100 Subject: [PATCH 5/5] Refactor IsolationModePicker: update trigger label on enable/disable --- .../sessions/contrib/chat/browser/sessionTargetPicker.ts | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/vs/sessions/contrib/chat/browser/sessionTargetPicker.ts b/src/vs/sessions/contrib/chat/browser/sessionTargetPicker.ts index 8112cee73752d..2406e26e0cd11 100644 --- a/src/vs/sessions/contrib/chat/browser/sessionTargetPicker.ts +++ b/src/vs/sessions/contrib/chat/browser/sessionTargetPicker.ts @@ -232,13 +232,7 @@ export class IsolationModePicker extends Disposable { */ setEnabled(enabled: boolean): void { this._enabled = enabled; - if (this._slotElement) { - this._slotElement.classList.toggle('disabled', !enabled); - } - if (this._triggerElement) { - this._triggerElement.tabIndex = enabled ? 0 : -1; - this._triggerElement.setAttribute('aria-disabled', String(!enabled)); - } + this._updateTriggerLabel(); } private _showPicker(): void {