mirror of
https://github.com/microsoft/vscode.git
synced 2026-04-02 08:15:56 +01:00
Refactor new chat view: rename to workspace, separate session type and isolation pickers (#302797)
* refactor: rename SessionProject to SessionWorkspace and update related references * fix: update filter placeholder text in project picker to "Search Workspaces..." * feat: add SessionWorkspace class to represent workspaces for sessions * feat: implement ProjectPicker class for unified project selection * fix: update SessionTargetType to use 'copilot-cli' instead of 'cli' * refactor: rename targetMode to isolationMode in NewSession and related classes * refactor: rename LocalNewSession to CopilotCLISession and update related references * feat: set project in session type picker for local sessions * fix: ensure project is set in session type picker for both remote and local sessions * fix: reset isolation mode to worktree when isolation option is disabled * delete file
This commit is contained in:
committed by
GitHub
parent
819f0cd46c
commit
44264fdeae
@@ -58,7 +58,7 @@ import { ACTIVE_GROUP, IEditorService, SIDE_GROUP } from '../../../../workbench/
|
||||
import { IExtensionService } from '../../../../workbench/services/extensions/common/extensions.js';
|
||||
import { IWorkbenchLayoutService } from '../../../../workbench/services/layout/browser/layoutService.js';
|
||||
import { IActiveSessionItem, ISessionsManagementService } from '../../sessions/browser/sessionsManagementService.js';
|
||||
import { GITHUB_REMOTE_FILE_SCHEME } from '../../sessions/common/sessionProject.js';
|
||||
import { GITHUB_REMOTE_FILE_SCHEME } from '../../sessions/common/sessionWorkspace.js';
|
||||
import { CodeReviewStateKind, getCodeReviewFilesFromSessionChanges, getCodeReviewVersion, ICodeReviewService, PRReviewStateKind } from '../../codeReview/browser/codeReviewService.js';
|
||||
import { IGitRepository, IGitService } from '../../../../workbench/contrib/git/common/gitService.js';
|
||||
import { IGitHubService } from '../../github/browser/githubService.js';
|
||||
|
||||
@@ -178,7 +178,7 @@
|
||||
}
|
||||
|
||||
/* Prominent project picker button */
|
||||
.sessions-chat-picker-slot.sessions-chat-project-picker .action-label {
|
||||
.sessions-chat-picker-slot.sessions-chat-workspace-picker .action-label {
|
||||
height: auto;
|
||||
padding: 8px 20px;
|
||||
font-size: 15px;
|
||||
@@ -188,16 +188,16 @@
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.sessions-chat-picker-slot.sessions-chat-project-picker .action-label:hover {
|
||||
.sessions-chat-picker-slot.sessions-chat-workspace-picker .action-label:hover {
|
||||
background-color: var(--vscode-button-secondaryHoverBackground);
|
||||
}
|
||||
|
||||
.sessions-chat-picker-slot.sessions-chat-project-picker .action-label .codicon {
|
||||
.sessions-chat-picker-slot.sessions-chat-workspace-picker .action-label .codicon {
|
||||
font-size: 18px;
|
||||
margin-right: 6px;
|
||||
}
|
||||
|
||||
.sessions-chat-picker-slot.sessions-chat-project-picker .action-label .sessions-chat-dropdown-label {
|
||||
.sessions-chat-picker-slot.sessions-chat-workspace-picker .action-label .sessions-chat-dropdown-label {
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
|
||||
@@ -54,13 +54,12 @@ import { ContextMenuController } from '../../../../editor/contrib/contextmenu/br
|
||||
import { getSimpleEditorOptions } from '../../../../workbench/contrib/codeEditor/browser/simpleEditorOptions.js';
|
||||
import { NewChatContextAttachments } from './newChatContextAttachments.js';
|
||||
import { IGitService } from '../../../../workbench/contrib/git/common/gitService.js';
|
||||
import { TargetPicker } from './sessionTargetPicker.js';
|
||||
import { SessionTypePicker, IsolationPicker } from './sessionTargetPicker.js';
|
||||
import { BranchPicker } from './branchPicker.js';
|
||||
import { SyncIndicator } from './syncIndicator.js';
|
||||
import { INewSession, ISessionOptionGroup, RemoteNewSession } from './newSession.js';
|
||||
import { CloudModelPicker } from './modelPicker.js';
|
||||
import { ProjectPicker } from './projectPicker.js';
|
||||
import { SessionProject } from '../../sessions/common/sessionProject.js';
|
||||
import { WorkspacePicker } from './workspacePicker.js';
|
||||
import { SessionWorkspace } from '../../sessions/common/sessionWorkspace.js';
|
||||
import { ModePicker } from './modePicker.js';
|
||||
import { getErrorMessage } from '../../../../base/common/errors.js';
|
||||
import { SlashCommandHandler } from './slashCommands.js';
|
||||
@@ -99,10 +98,10 @@ interface INewChatWidgetOptions {
|
||||
*/
|
||||
class NewChatWidget extends Disposable implements IHistoryNavigationWidget {
|
||||
|
||||
private readonly _projectPicker: ProjectPicker;
|
||||
private readonly _targetPicker: TargetPicker;
|
||||
private readonly _workspacePicker: WorkspacePicker;
|
||||
private readonly _sessionTypePicker: SessionTypePicker;
|
||||
private readonly _branchPicker: BranchPicker;
|
||||
private readonly _syncIndicator: SyncIndicator;
|
||||
private readonly _isolationPicker: IsolationPicker;
|
||||
private readonly _options: INewChatWidgetOptions;
|
||||
|
||||
// IHistoryNavigationWidget
|
||||
@@ -185,17 +184,17 @@ class NewChatWidget extends Disposable implements IHistoryNavigationWidget {
|
||||
super();
|
||||
this._history = this._register(this.instantiationService.createInstance(ChatHistoryNavigator, ChatAgentLocation.Chat));
|
||||
this._contextAttachments = this._register(this.instantiationService.createInstance(NewChatContextAttachments));
|
||||
this._projectPicker = this._register(this.instantiationService.createInstance(ProjectPicker));
|
||||
this._workspacePicker = this._register(this.instantiationService.createInstance(WorkspacePicker));
|
||||
this._permissionPicker = this._register(this.instantiationService.createInstance(NewChatPermissionPicker));
|
||||
this._cloudModelPicker = this._register(this.instantiationService.createInstance(CloudModelPicker));
|
||||
this._modePicker = this._register(this.instantiationService.createInstance(ModePicker));
|
||||
this._targetPicker = this._register(this.instantiationService.createInstance(TargetPicker));
|
||||
this._sessionTypePicker = this._register(this.instantiationService.createInstance(SessionTypePicker));
|
||||
this._branchPicker = this._register(this.instantiationService.createInstance(BranchPicker));
|
||||
this._syncIndicator = this._register(this.instantiationService.createInstance(SyncIndicator));
|
||||
this._isolationPicker = this._register(this.instantiationService.createInstance(IsolationPicker));
|
||||
this._options = options;
|
||||
|
||||
// When a project is selected, infer the target and create a new session
|
||||
this._register(this._projectPicker.onDidSelectProject(async (project) => {
|
||||
this._register(this._workspacePicker.onDidSelectProject(async (project) => {
|
||||
await this._onProjectSelected(project);
|
||||
this._updateDraftState();
|
||||
this._focusEditor();
|
||||
@@ -208,15 +207,26 @@ class NewChatWidget extends Disposable implements IHistoryNavigationWidget {
|
||||
|
||||
this._register(this._branchPicker.onDidChange((branch) => {
|
||||
this._newSession.value?.setBranch(branch);
|
||||
this._syncIndicator.setBranch(branch);
|
||||
this._updateDraftState();
|
||||
this._focusEditor();
|
||||
}));
|
||||
|
||||
this._register(this._targetPicker.onDidChange((mode) => {
|
||||
this._newSession.value?.setTargetMode(mode);
|
||||
this._register(this._sessionTypePicker.onDidChange((target) => {
|
||||
if (target === 'cloud') {
|
||||
this._isolationPicker.setVisible(false);
|
||||
this._branchPicker.setVisible(false);
|
||||
} else {
|
||||
this._newSession.value?.setIsolationMode(this._isolationPicker.isolationMode);
|
||||
this._isolationPicker.setVisible(true);
|
||||
this._branchPicker.setVisible(this._isolationPicker.isWorktree);
|
||||
}
|
||||
this._updateDraftState();
|
||||
this._focusEditor();
|
||||
}));
|
||||
|
||||
this._register(this._isolationPicker.onDidChange((mode) => {
|
||||
this._newSession.value?.setIsolationMode(mode);
|
||||
this._branchPicker.setVisible(mode === 'worktree');
|
||||
this._syncIndicator.setVisible(mode === 'worktree');
|
||||
this._updateDraftState();
|
||||
this._focusEditor();
|
||||
}));
|
||||
@@ -281,12 +291,12 @@ class NewChatWidget extends Disposable implements IHistoryNavigationWidget {
|
||||
|
||||
// Isolation mode and branch pickers (below the input, shown when Local target is selected)
|
||||
const isolationContainer = dom.append(welcomeElement, dom.$('.chat-full-welcome-local-mode'));
|
||||
this._targetPicker.render(isolationContainer);
|
||||
this._sessionTypePicker.render(isolationContainer);
|
||||
this._permissionPicker.render(isolationContainer);
|
||||
dom.append(isolationContainer, dom.$('.sessions-chat-local-mode-spacer'));
|
||||
const branchContainer = dom.append(isolationContainer, dom.$('.sessions-chat-local-mode-right'));
|
||||
this._isolationPicker.render(branchContainer);
|
||||
this._branchPicker.render(branchContainer);
|
||||
this._syncIndicator.render(branchContainer);
|
||||
|
||||
// Render project picker & extension pickers
|
||||
this._renderOptionGroupPickers();
|
||||
@@ -298,7 +308,7 @@ class NewChatWidget extends Disposable implements IHistoryNavigationWidget {
|
||||
this._restoreState();
|
||||
|
||||
// Create initial session
|
||||
const restoredProject = this._projectPicker.selectedProject;
|
||||
const restoredProject = this._workspacePicker.selectedProject;
|
||||
if (restoredProject) {
|
||||
this._onProjectSelected(restoredProject);
|
||||
} else {
|
||||
@@ -314,7 +324,7 @@ class NewChatWidget extends Disposable implements IHistoryNavigationWidget {
|
||||
}, { once: true }));
|
||||
}
|
||||
|
||||
private async _createNewSession(project?: SessionProject): Promise<void> {
|
||||
private async _createNewSession(project?: SessionWorkspace): Promise<void> {
|
||||
const target = project?.isRepo ? AgentSessionProviders.Cloud : AgentSessionProviders.Background;
|
||||
|
||||
const resource = getResourceForNewChatSession({
|
||||
@@ -358,8 +368,9 @@ class NewChatWidget extends Disposable implements IHistoryNavigationWidget {
|
||||
}
|
||||
}));
|
||||
|
||||
this._sessionTypePicker.setProject(session.project);
|
||||
|
||||
if (session instanceof RemoteNewSession) {
|
||||
this._targetPicker.setProject(session.project);
|
||||
this._renderRemoteSessionPickers(session, true);
|
||||
listeners.add(session.onDidChangeOptionGroups(() => {
|
||||
this._renderRemoteSessionPickers(session);
|
||||
@@ -382,7 +393,6 @@ class NewChatWidget extends Disposable implements IHistoryNavigationWidget {
|
||||
this._repositoryLoading = true;
|
||||
this._updateInputLoadingState();
|
||||
this._branchPicker.setRepository(undefined);
|
||||
this._syncIndicator.setRepository(undefined);
|
||||
this._modePicker.reset();
|
||||
|
||||
this.gitService.openRepository(folderUri).then(repository => {
|
||||
@@ -397,11 +407,10 @@ class NewChatWidget extends Disposable implements IHistoryNavigationWidget {
|
||||
session.setProject(session.project.withRepository(repository));
|
||||
}
|
||||
|
||||
this._targetPicker.setProject(session?.project);
|
||||
this._sessionTypePicker.setProject(session?.project);
|
||||
this._isolationPicker.setHasGitRepo(!!repository);
|
||||
this._branchPicker.setRepository(repository);
|
||||
this._branchPicker.setVisible(!!repository && this._targetPicker.isWorktree);
|
||||
this._syncIndicator.setRepository(repository);
|
||||
this._syncIndicator.setVisible(!!repository && this._targetPicker.isWorktree);
|
||||
this._branchPicker.setVisible(!!repository && this._sessionTypePicker.isCli && this._isolationPicker.isWorktree);
|
||||
this._modePicker.reset();
|
||||
}).catch(e => {
|
||||
if (cts.token.isCancellationRequested) {
|
||||
@@ -410,11 +419,10 @@ class NewChatWidget extends Disposable implements IHistoryNavigationWidget {
|
||||
this.logService.warn(`Failed to open repository at ${folderUri.toString()}`, getErrorMessage(e));
|
||||
this._repositoryLoading = false;
|
||||
this._updateInputLoadingState();
|
||||
this._targetPicker.setProject(undefined);
|
||||
this._sessionTypePicker.setProject(undefined);
|
||||
this._isolationPicker.setHasGitRepo(false);
|
||||
this._branchPicker.setRepository(undefined);
|
||||
this._branchPicker.setVisible(false);
|
||||
this._syncIndicator.setRepository(undefined);
|
||||
this._syncIndicator.setVisible(false);
|
||||
this._modePicker.reset();
|
||||
});
|
||||
}
|
||||
@@ -677,7 +685,7 @@ class NewChatWidget extends Disposable implements IHistoryNavigationWidget {
|
||||
const pickersRow = dom.append(this._pickersContainer, dom.$('.chat-full-welcome-pickers'));
|
||||
|
||||
// Project picker (unified folder + repo picker)
|
||||
this._projectPicker.render(pickersRow);
|
||||
this._workspacePicker.render(pickersRow);
|
||||
}
|
||||
|
||||
// --- Local session pickers ---
|
||||
@@ -703,7 +711,7 @@ class NewChatWidget extends Disposable implements IHistoryNavigationWidget {
|
||||
this._modePicker.setVisible(false);
|
||||
this._permissionPicker.setVisible(false);
|
||||
this._branchPicker.setVisible(false);
|
||||
this._syncIndicator.setVisible(false);
|
||||
this._isolationPicker.setVisible(false);
|
||||
|
||||
this._cloudModelPicker.setSession(session);
|
||||
this._cloudModelPicker.setVisible(true);
|
||||
@@ -957,20 +965,20 @@ class NewChatWidget extends Disposable implements IHistoryNavigationWidget {
|
||||
}
|
||||
|
||||
private _openRepoOrFolderPicker(_sessionType: AgentSessionProviders): void {
|
||||
this._projectPicker.showPicker();
|
||||
this._workspacePicker.showPicker();
|
||||
}
|
||||
|
||||
private async _requestFolderTrust(folderUri: URI, previousProject?: SessionProject): Promise<boolean> {
|
||||
private async _requestFolderTrust(folderUri: URI, previousProject?: SessionWorkspace): Promise<boolean> {
|
||||
const trusted = await this.workspaceTrustRequestService.requestResourcesTrust({
|
||||
uri: folderUri,
|
||||
message: localize('trustFolderMessage', "An agent session will be able to read files, run commands, and make changes in this folder."),
|
||||
});
|
||||
if (!trusted) {
|
||||
this._projectPicker.removeFromRecents(folderUri);
|
||||
this._workspacePicker.removeFromRecents(folderUri);
|
||||
if (previousProject) {
|
||||
this._projectPicker.setSelectedProject(previousProject, false);
|
||||
this._workspacePicker.setSelectedProject(previousProject, false);
|
||||
} else {
|
||||
this._projectPicker.clearSelection();
|
||||
this._workspacePicker.clearSelection();
|
||||
}
|
||||
}
|
||||
return !!trusted;
|
||||
@@ -996,8 +1004,8 @@ class NewChatWidget extends Disposable implements IHistoryNavigationWidget {
|
||||
}
|
||||
if (draft.projectUri) {
|
||||
try {
|
||||
const project = new SessionProject(URI.revive(draft.projectUri));
|
||||
this._projectPicker.setSelectedProject(project, false);
|
||||
const project = new SessionWorkspace(URI.revive(draft.projectUri));
|
||||
this._workspacePicker.setSelectedProject(project, false);
|
||||
} catch { /* ignore */ }
|
||||
}
|
||||
}
|
||||
@@ -1054,7 +1062,7 @@ class NewChatWidget extends Disposable implements IHistoryNavigationWidget {
|
||||
* Infers the session target from the selection kind, creates a new session,
|
||||
* and shows/hides pickers accordingly.
|
||||
*/
|
||||
private async _onProjectSelected(project: SessionProject): Promise<void> {
|
||||
private async _onProjectSelected(project: SessionWorkspace): Promise<void> {
|
||||
// Cancel any in-flight project selection
|
||||
this._projectSelectionCts.value?.cancel();
|
||||
const cts = this._projectSelectionCts.value = new CancellationTokenSource();
|
||||
|
||||
@@ -8,15 +8,15 @@ import { Disposable, IDisposable } from '../../../../base/common/lifecycle.js';
|
||||
import { URI } from '../../../../base/common/uri.js';
|
||||
import { ILogService } from '../../../../platform/log/common/log.js';
|
||||
import { IChatSessionProviderOptionGroup, IChatSessionProviderOptionItem, IChatSessionsService } from '../../../../workbench/contrib/chat/common/chatSessionsService.js';
|
||||
import { TargetMode } from './sessionTargetPicker.js';
|
||||
import { SessionProject } from '../../sessions/common/sessionProject.js';
|
||||
import { IsolationMode } from './sessionTargetPicker.js';
|
||||
import { SessionWorkspace } from '../../sessions/common/sessionWorkspace.js';
|
||||
import { AgentSessionProviders } from '../../../../workbench/contrib/chat/browser/agentSessions/agentSessions.js';
|
||||
import { ContextKeyExpr, IContextKeyService } from '../../../../platform/contextkey/common/contextkey.js';
|
||||
|
||||
import { IChatRequestVariableEntry } from '../../../../workbench/contrib/chat/common/attachments/chatVariableEntries.js';
|
||||
import { IChatMode } from '../../../../workbench/contrib/chat/common/chatModes.js';
|
||||
|
||||
export type NewSessionChangeType = 'repoUri' | 'targetMode' | 'branch' | 'options' | 'disabled' | 'agent';
|
||||
export type NewSessionChangeType = 'repoUri' | 'isolationMode' | 'branch' | 'options' | 'disabled' | 'agent';
|
||||
|
||||
/**
|
||||
* Represents a resolved option group with its current selected value.
|
||||
@@ -28,14 +28,14 @@ export interface ISessionOptionGroup {
|
||||
|
||||
/**
|
||||
* A new session represents a session being configured before the first
|
||||
* request is sent. It holds the user's selections (repoUri, targetMode)
|
||||
* request is sent. It holds the user's selections (repoUri, isolationMode)
|
||||
* and fires a single event when any property changes.
|
||||
*/
|
||||
export interface INewSession extends IDisposable {
|
||||
readonly resource: URI;
|
||||
readonly target: AgentSessionProviders;
|
||||
readonly project: SessionProject | undefined;
|
||||
readonly targetMode: TargetMode;
|
||||
readonly project: SessionWorkspace | undefined;
|
||||
readonly isolationMode: IsolationMode | undefined;
|
||||
readonly branch: string | undefined;
|
||||
readonly modelId: string | undefined;
|
||||
readonly mode: IChatMode | undefined;
|
||||
@@ -44,8 +44,8 @@ export interface INewSession extends IDisposable {
|
||||
readonly selectedOptions: ReadonlyMap<string, IChatSessionProviderOptionItem>;
|
||||
readonly disabled: boolean;
|
||||
readonly onDidChange: Event<NewSessionChangeType>;
|
||||
setProject(project: SessionProject): void;
|
||||
setTargetMode(mode: TargetMode): void;
|
||||
setProject(project: SessionWorkspace): void;
|
||||
setIsolationMode(mode: IsolationMode): void;
|
||||
setBranch(branch: string | undefined): void;
|
||||
setModelId(modelId: string | undefined): void;
|
||||
setMode(mode: IChatMode | undefined): void;
|
||||
@@ -61,14 +61,14 @@ const AGENT_OPTION_ID = 'agent';
|
||||
|
||||
/**
|
||||
* Local new session for Background agent sessions.
|
||||
* Fires `onDidChange` for both `repoUri` and `targetMode` changes.
|
||||
* Fires `onDidChange` for both `repoUri` and `isolationMode` changes.
|
||||
* Notifies the extension service with session options for each property change.
|
||||
*/
|
||||
export class LocalNewSession extends Disposable implements INewSession {
|
||||
export class CopilotCLISession extends Disposable implements INewSession {
|
||||
|
||||
private _repoUri: URI | undefined;
|
||||
private _project: SessionProject | undefined;
|
||||
private _targetMode: TargetMode;
|
||||
private _project: SessionWorkspace | undefined;
|
||||
private _isolationMode: IsolationMode;
|
||||
private _branch: string | undefined;
|
||||
private _modelId: string | undefined;
|
||||
private _mode: IChatMode | undefined;
|
||||
@@ -81,8 +81,8 @@ export class LocalNewSession extends Disposable implements INewSession {
|
||||
readonly target = AgentSessionProviders.Background;
|
||||
readonly selectedOptions = new Map<string, IChatSessionProviderOptionItem>();
|
||||
|
||||
get project(): SessionProject | undefined { return this._project; }
|
||||
get targetMode(): TargetMode { return this._targetMode; }
|
||||
get project(): SessionWorkspace | undefined { return this._project; }
|
||||
get isolationMode(): IsolationMode { return this._isolationMode; }
|
||||
get branch(): string | undefined { return this._branch; }
|
||||
get modelId(): string | undefined { return this._modelId; }
|
||||
get mode(): IChatMode | undefined { return this._mode; }
|
||||
@@ -92,7 +92,7 @@ export class LocalNewSession extends Disposable implements INewSession {
|
||||
if (!this._repoUri) {
|
||||
return true;
|
||||
}
|
||||
if (this._targetMode === 'worktree' && !this._branch) {
|
||||
if (this._isolationMode === 'worktree' && !this._branch) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -109,24 +109,24 @@ export class LocalNewSession extends Disposable implements INewSession {
|
||||
this._repoUri = defaultRepoUri;
|
||||
this.setOption(REPOSITORY_OPTION_ID, defaultRepoUri.fsPath);
|
||||
}
|
||||
this._targetMode = 'worktree';
|
||||
this._isolationMode = 'worktree';
|
||||
this.setOption(ISOLATION_OPTION_ID, 'worktree');
|
||||
}
|
||||
|
||||
setProject(project: SessionProject): void {
|
||||
setProject(project: SessionWorkspace): void {
|
||||
this._project = project;
|
||||
this._repoUri = project.uri;
|
||||
this.setTargetMode('worktree');
|
||||
this.setIsolationMode('worktree');
|
||||
this._branch = undefined;
|
||||
this._onDidChange.fire('repoUri');
|
||||
this._onDidChange.fire('disabled');
|
||||
this.setOption(REPOSITORY_OPTION_ID, project.uri.fsPath);
|
||||
}
|
||||
|
||||
setTargetMode(mode: TargetMode): void {
|
||||
if (this._targetMode !== mode) {
|
||||
this._targetMode = mode;
|
||||
this._onDidChange.fire('targetMode');
|
||||
setIsolationMode(mode: IsolationMode): void {
|
||||
if (this._isolationMode !== mode) {
|
||||
this._isolationMode = mode;
|
||||
this._onDidChange.fire('isolationMode');
|
||||
this._onDidChange.fire('disabled');
|
||||
this.setOption(ISOLATION_OPTION_ID, mode);
|
||||
}
|
||||
@@ -183,7 +183,7 @@ export class LocalNewSession extends Disposable implements INewSession {
|
||||
export class RemoteNewSession extends Disposable implements INewSession {
|
||||
|
||||
private _repoUri: URI | undefined;
|
||||
private _project: SessionProject | undefined;
|
||||
private _project: SessionWorkspace | undefined;
|
||||
private _modelId: string | undefined;
|
||||
private _query: string | undefined;
|
||||
private _attachedContext: IChatRequestVariableEntry[] | undefined;
|
||||
@@ -196,8 +196,8 @@ export class RemoteNewSession extends Disposable implements INewSession {
|
||||
|
||||
readonly selectedOptions = new Map<string, IChatSessionProviderOptionItem>();
|
||||
|
||||
get project(): SessionProject | undefined { return this._project; }
|
||||
get targetMode(): TargetMode { return 'cloud'; }
|
||||
get project(): SessionWorkspace | undefined { return this._project; }
|
||||
get isolationMode(): undefined { return undefined; }
|
||||
get branch(): string | undefined { return undefined; }
|
||||
get modelId(): string | undefined { return this._modelId; }
|
||||
get mode(): IChatMode | undefined { return undefined; }
|
||||
@@ -232,7 +232,7 @@ export class RemoteNewSession extends Disposable implements INewSession {
|
||||
}));
|
||||
}
|
||||
|
||||
setProject(project: SessionProject): void {
|
||||
setProject(project: SessionWorkspace): void {
|
||||
this._project = project;
|
||||
this._repoUri = project.uri;
|
||||
this._onDidChange.fire('repoUri');
|
||||
@@ -241,7 +241,7 @@ export class RemoteNewSession extends Disposable implements INewSession {
|
||||
this.setOption('repositories', { id, name: id });
|
||||
}
|
||||
|
||||
setTargetMode(_mode: TargetMode): void {
|
||||
setIsolationMode(_mode: IsolationMode): void {
|
||||
// No-op for remote sessions
|
||||
}
|
||||
|
||||
|
||||
@@ -12,49 +12,173 @@ import { localize } from '../../../../nls.js';
|
||||
import { IActionWidgetService } from '../../../../platform/actionWidget/browser/actionWidget.js';
|
||||
import { ActionListItemKind, IActionListDelegate, IActionListItem } from '../../../../platform/actionWidget/browser/actionList.js';
|
||||
import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js';
|
||||
import { SessionProject } from '../../sessions/common/sessionProject.js';
|
||||
import { SessionWorkspace } from '../../sessions/common/sessionWorkspace.js';
|
||||
|
||||
// #region --- Types ---
|
||||
|
||||
export type SessionTargetType = 'copilot-cli' | 'cloud';
|
||||
export type IsolationMode = 'worktree' | 'workspace';
|
||||
|
||||
// #endregion
|
||||
|
||||
// #region --- Target Picker ---
|
||||
|
||||
export type TargetMode = 'worktree' | 'workspace' | 'cloud';
|
||||
|
||||
/**
|
||||
* A self-contained widget for selecting the session target mode.
|
||||
* A self-contained widget for selecting the session target type.
|
||||
*
|
||||
* Options:
|
||||
* - **Worktree** (`worktree`) — shown when a folder with a git repo is selected
|
||||
* - **Folder** (`workspace`) — shown only when isolation option is enabled
|
||||
* - **Cloud** (`cloud`) — shown and auto-selected when a repository is picked; disabled
|
||||
* - **Copilot CLI** (`cli`) — local agent session
|
||||
* - **Cloud** (`cloud`) — remote/cloud agent session
|
||||
*
|
||||
* Emits `onDidChange` with the selected `TargetMode` when the user picks an option.
|
||||
* The target is determined by the project type (folder → CLI, repo → Cloud).
|
||||
* Emits `onDidChange` with the selected `SessionTargetType` when the target changes.
|
||||
*/
|
||||
export class TargetPicker extends Disposable {
|
||||
export class SessionTypePicker extends Disposable {
|
||||
|
||||
private _targetMode: TargetMode = 'worktree';
|
||||
private _project: SessionProject | undefined;
|
||||
private _isolationOptionEnabled: boolean = true;
|
||||
private _sessionTarget: SessionTargetType = 'copilot-cli';
|
||||
private _project: SessionWorkspace | undefined;
|
||||
|
||||
private readonly _onDidChange = this._register(new Emitter<TargetMode>());
|
||||
readonly onDidChange: Event<TargetMode> = this._onDidChange.event;
|
||||
private readonly _onDidChange = this._register(new Emitter<SessionTargetType>());
|
||||
readonly onDidChange: Event<SessionTargetType> = this._onDidChange.event;
|
||||
|
||||
private readonly _renderDisposables = this._register(new DisposableStore());
|
||||
private _slotElement: HTMLElement | undefined;
|
||||
private _triggerElement: HTMLElement | undefined;
|
||||
|
||||
get targetMode(): TargetMode {
|
||||
return this._targetMode;
|
||||
get sessionTarget(): SessionTargetType {
|
||||
return this._sessionTarget;
|
||||
}
|
||||
|
||||
get isWorktree(): boolean {
|
||||
return this._targetMode === 'worktree';
|
||||
}
|
||||
|
||||
get isFolder(): boolean {
|
||||
return this._targetMode === 'workspace';
|
||||
get isCli(): boolean {
|
||||
return this._sessionTarget === 'copilot-cli';
|
||||
}
|
||||
|
||||
get isCloud(): boolean {
|
||||
return this._targetMode === 'cloud';
|
||||
return this._sessionTarget === 'cloud';
|
||||
}
|
||||
|
||||
constructor(
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the current project context. Determines the target type:
|
||||
* - Repo project → cloud
|
||||
* - Folder project → cli
|
||||
* - No project → retains current target
|
||||
*/
|
||||
setProject(project: SessionWorkspace | undefined): void {
|
||||
this._project = project;
|
||||
this._updateTarget();
|
||||
this._updateTriggerLabel();
|
||||
}
|
||||
|
||||
private _updateTarget(): void {
|
||||
if (this._project?.isRepo) {
|
||||
this._setTarget('cloud');
|
||||
return;
|
||||
}
|
||||
|
||||
if (this._project?.isFolder) {
|
||||
this._setTarget('copilot-cli');
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
render(container: HTMLElement): void {
|
||||
this._renderDisposables.clear();
|
||||
|
||||
const slot = dom.append(container, dom.$('.sessions-chat-picker-slot'));
|
||||
this._slotElement = slot;
|
||||
this._renderDisposables.add({ dispose: () => slot.remove() });
|
||||
|
||||
const trigger = dom.append(slot, dom.$('a.action-label'));
|
||||
trigger.tabIndex = -1;
|
||||
trigger.role = 'button';
|
||||
trigger.setAttribute('aria-disabled', 'true');
|
||||
this._triggerElement = trigger;
|
||||
this._updateTriggerLabel();
|
||||
}
|
||||
|
||||
private _setTarget(target: SessionTargetType): void {
|
||||
if (this._sessionTarget !== target) {
|
||||
this._sessionTarget = target;
|
||||
this._updateTriggerLabel();
|
||||
this._onDidChange.fire(target);
|
||||
}
|
||||
}
|
||||
|
||||
private _updateTriggerLabel(): void {
|
||||
if (!this._triggerElement) {
|
||||
return;
|
||||
}
|
||||
|
||||
dom.clearNode(this._triggerElement);
|
||||
|
||||
let modeIcon;
|
||||
let modeLabel: string;
|
||||
|
||||
switch (this._sessionTarget) {
|
||||
case 'cloud':
|
||||
modeIcon = Codicon.cloud;
|
||||
modeLabel = localize('sessionTarget.cloud', "Cloud");
|
||||
break;
|
||||
case 'copilot-cli':
|
||||
default:
|
||||
modeIcon = Codicon.worktree;
|
||||
modeLabel = localize('sessionTarget.cli', "Copilot CLI");
|
||||
break;
|
||||
}
|
||||
|
||||
dom.append(this._triggerElement, renderIcon(modeIcon));
|
||||
const labelSpan = dom.append(this._triggerElement, dom.$('span.sessions-chat-dropdown-label'));
|
||||
labelSpan.textContent = modeLabel;
|
||||
|
||||
this._slotElement?.classList.toggle('disabled', true);
|
||||
}
|
||||
}
|
||||
|
||||
// #endregion
|
||||
|
||||
// #region --- Isolation Picker ---
|
||||
|
||||
/**
|
||||
* A self-contained widget for selecting the isolation mode.
|
||||
*
|
||||
* Options:
|
||||
* - **Worktree** (`worktree`) — run in a git worktree
|
||||
* - **Folder** (`workspace`) — run directly in the folder
|
||||
*
|
||||
* Only visible when isolation option is enabled, project has a git repo,
|
||||
* and the target is CLI.
|
||||
*
|
||||
* Emits `onDidChange` with the selected `IsolationMode` when the user picks an option.
|
||||
*/
|
||||
export class IsolationPicker extends Disposable {
|
||||
|
||||
private _isolationMode: IsolationMode = 'worktree';
|
||||
private _hasGitRepo = false;
|
||||
private _visible = true;
|
||||
private _isolationOptionEnabled: boolean;
|
||||
|
||||
private readonly _onDidChange = this._register(new Emitter<IsolationMode>());
|
||||
readonly onDidChange: Event<IsolationMode> = this._onDidChange.event;
|
||||
|
||||
private readonly _renderDisposables = this._register(new DisposableStore());
|
||||
private _slotElement: HTMLElement | undefined;
|
||||
private _triggerElement: HTMLElement | undefined;
|
||||
|
||||
get isolationMode(): IsolationMode {
|
||||
return this._isolationMode;
|
||||
}
|
||||
|
||||
get isWorktree(): boolean {
|
||||
return this._isolationMode === 'worktree';
|
||||
}
|
||||
|
||||
get isFolder(): boolean {
|
||||
return this._isolationMode === 'workspace';
|
||||
}
|
||||
|
||||
constructor(
|
||||
@@ -67,35 +191,37 @@ export class TargetPicker extends Disposable {
|
||||
this._register(this.configurationService.onDidChangeConfiguration(e => {
|
||||
if (e.affectsConfiguration('github.copilot.chat.cli.isolationOption.enabled')) {
|
||||
this._isolationOptionEnabled = this.configurationService.getValue<boolean>('github.copilot.chat.cli.isolationOption.enabled') !== false;
|
||||
this._updateMode();
|
||||
if (!this._isolationOptionEnabled) {
|
||||
// Reset to worktree when isolation option is disabled
|
||||
this._setMode('worktree');
|
||||
}
|
||||
this._updateVisibility();
|
||||
this._updateTriggerLabel();
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the current project context. Determines the available target modes:
|
||||
* - Repo project → cloud mode (disabled picker)
|
||||
* - Folder with git repo → retains current local mode (worktree/folder)
|
||||
* - Folder without git repo → folder mode only
|
||||
* - No project → retains current mode
|
||||
* Sets whether the project has a git repository.
|
||||
* Resets isolation mode to the appropriate default.
|
||||
*/
|
||||
setProject(project: SessionProject | undefined): void {
|
||||
this._project = project;
|
||||
this._updateMode();
|
||||
setHasGitRepo(hasRepo: boolean): void {
|
||||
this._hasGitRepo = hasRepo;
|
||||
if (!hasRepo) {
|
||||
this._setMode('workspace');
|
||||
} else {
|
||||
this._setMode('worktree');
|
||||
}
|
||||
this._updateVisibility();
|
||||
this._updateTriggerLabel();
|
||||
}
|
||||
|
||||
private _updateMode(): void {
|
||||
if (this._project?.isRepo) {
|
||||
this._setMode('cloud');
|
||||
return;
|
||||
}
|
||||
|
||||
if (this._project?.isFolder) {
|
||||
this._setMode(this._project.repository ? 'worktree' : 'workspace');
|
||||
return;
|
||||
}
|
||||
/**
|
||||
* Sets external visibility (e.g. hidden when target is Cloud).
|
||||
*/
|
||||
setVisible(visible: boolean): void {
|
||||
this._visible = visible;
|
||||
this._updateVisibility();
|
||||
}
|
||||
|
||||
render(container: HTMLElement): void {
|
||||
@@ -110,6 +236,7 @@ export class TargetPicker extends Disposable {
|
||||
trigger.role = 'button';
|
||||
this._triggerElement = trigger;
|
||||
this._updateTriggerLabel();
|
||||
this._updateVisibility();
|
||||
|
||||
this._renderDisposables.add(dom.addDisposableListener(trigger, dom.EventType.CLICK, (e) => {
|
||||
dom.EventHelper.stop(e, true);
|
||||
@@ -125,35 +252,31 @@ export class TargetPicker extends Disposable {
|
||||
}
|
||||
|
||||
private _showPicker(): void {
|
||||
if (!this._triggerElement || this.actionWidgetService.isVisible || this._targetMode === 'cloud') {
|
||||
if (!this._triggerElement || this.actionWidgetService.isVisible) {
|
||||
return;
|
||||
}
|
||||
|
||||
// No picker when there's no git repo — only Folder mode is available
|
||||
if (!this._project?.repository) {
|
||||
if (!this._hasGitRepo || !this._isolationOptionEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
const items: IActionListItem<TargetMode>[] = [
|
||||
const items: IActionListItem<IsolationMode>[] = [
|
||||
{
|
||||
kind: ActionListItemKind.Action,
|
||||
label: localize('targetMode.worktree', "Worktree"),
|
||||
label: localize('isolationMode.worktree', "Worktree"),
|
||||
group: { title: '', icon: Codicon.worktree },
|
||||
item: 'worktree',
|
||||
},
|
||||
];
|
||||
|
||||
if (this._isolationOptionEnabled) {
|
||||
items.push({
|
||||
{
|
||||
kind: ActionListItemKind.Action,
|
||||
label: localize('targetMode.folder', "Folder"),
|
||||
label: localize('isolationMode.folder', "Folder"),
|
||||
group: { title: '', icon: Codicon.folder },
|
||||
item: 'workspace',
|
||||
});
|
||||
}
|
||||
},
|
||||
];
|
||||
|
||||
const triggerElement = this._triggerElement;
|
||||
const delegate: IActionListDelegate<TargetMode> = {
|
||||
const delegate: IActionListDelegate<IsolationMode> = {
|
||||
onSelect: (mode) => {
|
||||
this.actionWidgetService.hide();
|
||||
this._setMode(mode);
|
||||
@@ -161,8 +284,8 @@ export class TargetPicker extends Disposable {
|
||||
onHide: () => { triggerElement.focus(); },
|
||||
};
|
||||
|
||||
this.actionWidgetService.show<TargetMode>(
|
||||
'targetPicker',
|
||||
this.actionWidgetService.show<IsolationMode>(
|
||||
'isolationPicker',
|
||||
false,
|
||||
items,
|
||||
delegate,
|
||||
@@ -171,19 +294,27 @@ export class TargetPicker extends Disposable {
|
||||
[],
|
||||
{
|
||||
getAriaLabel: (item) => item.label ?? '',
|
||||
getWidgetAriaLabel: () => localize('targetPicker.ariaLabel', "Target"),
|
||||
getWidgetAriaLabel: () => localize('isolationPicker.ariaLabel', "Isolation Mode"),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
private _setMode(mode: TargetMode): void {
|
||||
if (this._targetMode !== mode) {
|
||||
this._targetMode = mode;
|
||||
private _setMode(mode: IsolationMode): void {
|
||||
if (this._isolationMode !== mode) {
|
||||
this._isolationMode = mode;
|
||||
this._updateTriggerLabel();
|
||||
this._onDidChange.fire(mode);
|
||||
}
|
||||
}
|
||||
|
||||
private _updateVisibility(): void {
|
||||
if (!this._slotElement) {
|
||||
return;
|
||||
}
|
||||
const shouldShow = this._visible && this._hasGitRepo && this._isolationOptionEnabled;
|
||||
this._slotElement.style.display = shouldShow ? '' : 'none';
|
||||
}
|
||||
|
||||
private _updateTriggerLabel(): void {
|
||||
if (!this._triggerElement) {
|
||||
return;
|
||||
@@ -193,25 +324,16 @@ export class TargetPicker extends Disposable {
|
||||
|
||||
let modeIcon;
|
||||
let modeLabel: string;
|
||||
let isDisabled: boolean = true;
|
||||
|
||||
if (this._project?.isFolder && this._project.repository) {
|
||||
isDisabled = !this._isolationOptionEnabled;
|
||||
}
|
||||
|
||||
switch (this._targetMode) {
|
||||
case 'cloud':
|
||||
modeIcon = Codicon.cloud;
|
||||
modeLabel = localize('targetMode.cloud', "Cloud");
|
||||
break;
|
||||
switch (this._isolationMode) {
|
||||
case 'workspace':
|
||||
modeIcon = Codicon.folder;
|
||||
modeLabel = localize('targetMode.folder', "Folder");
|
||||
modeLabel = localize('isolationMode.folder', "Folder");
|
||||
break;
|
||||
case 'worktree':
|
||||
default:
|
||||
modeIcon = Codicon.worktree;
|
||||
modeLabel = localize('targetMode.worktree', "Worktree");
|
||||
modeLabel = localize('isolationMode.worktree', "Worktree");
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -219,12 +341,6 @@ export class TargetPicker extends Disposable {
|
||||
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);
|
||||
if (this._triggerElement) {
|
||||
this._triggerElement.tabIndex = isDisabled ? -1 : 0;
|
||||
this._triggerElement.setAttribute('aria-disabled', String(isDisabled));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ import { IFileDialogService } from '../../../../platform/dialogs/common/dialogs.
|
||||
import { IStorageService, StorageScope, StorageTarget } from '../../../../platform/storage/common/storage.js';
|
||||
import { IUriIdentityService } from '../../../../platform/uriIdentity/common/uriIdentity.js';
|
||||
import { renderIcon } from '../../../../base/browser/ui/iconLabel/iconLabels.js';
|
||||
import { GITHUB_REMOTE_FILE_SCHEME, SessionProject } from '../../sessions/common/sessionProject.js';
|
||||
import { GITHUB_REMOTE_FILE_SCHEME, SessionWorkspace } from '../../sessions/common/sessionWorkspace.js';
|
||||
|
||||
const OPEN_REPO_COMMAND = 'github.copilot.chat.cloudSessions.openRepository';
|
||||
const STORAGE_KEY_LAST_PROJECT = 'sessions.lastPickedProject';
|
||||
@@ -51,18 +51,18 @@ interface IStoredProject {
|
||||
* - "Browse Folders..." — opens a folder dialog
|
||||
* - "Browse Repositories..." — runs the cloud repository picker command
|
||||
*/
|
||||
export class ProjectPicker extends Disposable {
|
||||
export class WorkspacePicker extends Disposable {
|
||||
|
||||
private readonly _onDidSelectProject = this._register(new Emitter<SessionProject>());
|
||||
readonly onDidSelectProject: Event<SessionProject> = this._onDidSelectProject.event;
|
||||
private readonly _onDidSelectProject = this._register(new Emitter<SessionWorkspace>());
|
||||
readonly onDidSelectProject: Event<SessionWorkspace> = this._onDidSelectProject.event;
|
||||
|
||||
private _selectedProject: SessionProject | undefined;
|
||||
private _selectedProject: SessionWorkspace | undefined;
|
||||
private _recentProjects: IStoredProject[] = [];
|
||||
|
||||
private _triggerElement: HTMLElement | undefined;
|
||||
private readonly _renderDisposables = this._register(new DisposableStore());
|
||||
|
||||
get selectedProject(): SessionProject | undefined {
|
||||
get selectedProject(): SessionWorkspace | undefined {
|
||||
return this._selectedProject;
|
||||
}
|
||||
|
||||
@@ -134,7 +134,7 @@ export class ProjectPicker extends Disposable {
|
||||
try {
|
||||
const lastFolder = this.storageService.get(LEGACY_STORAGE_KEY_LAST_FOLDER, StorageScope.PROFILE);
|
||||
if (lastFolder) {
|
||||
this._selectedProject = new SessionProject(URI.parse(lastFolder));
|
||||
this._selectedProject = new SessionWorkspace(URI.parse(lastFolder));
|
||||
return;
|
||||
}
|
||||
} catch { /* ignore */ }
|
||||
@@ -143,7 +143,7 @@ export class ProjectPicker extends Disposable {
|
||||
const lastRepo = this.storageService.get(LEGACY_STORAGE_KEY_LAST_REPO, StorageScope.PROFILE);
|
||||
if (lastRepo) {
|
||||
const repo: { id: string; name: string } = JSON.parse(lastRepo);
|
||||
this._selectedProject = new SessionProject(URI.from({ scheme: GITHUB_REMOTE_FILE_SCHEME, authority: 'github', path: `/${repo.id}/HEAD` }));
|
||||
this._selectedProject = new SessionWorkspace(URI.from({ scheme: GITHUB_REMOTE_FILE_SCHEME, authority: 'github', path: `/${repo.id}/HEAD` }));
|
||||
}
|
||||
} catch { /* ignore */ }
|
||||
}
|
||||
@@ -155,7 +155,7 @@ export class ProjectPicker extends Disposable {
|
||||
render(container: HTMLElement): HTMLElement {
|
||||
this._renderDisposables.clear();
|
||||
|
||||
const slot = dom.append(container, dom.$('.sessions-chat-picker-slot.sessions-chat-project-picker'));
|
||||
const slot = dom.append(container, dom.$('.sessions-chat-picker-slot.sessions-chat-workspace-picker'));
|
||||
this._renderDisposables.add({ dispose: () => slot.remove() });
|
||||
|
||||
const trigger = dom.append(slot, dom.$('a.action-label'));
|
||||
@@ -207,10 +207,10 @@ export class ProjectPicker extends Disposable {
|
||||
onHide: () => { triggerElement.focus(); },
|
||||
};
|
||||
|
||||
const listOptions = showFilter ? { showFilter: true, filterPlaceholder: localize('projectPicker.filter', "Filter projects...") } : undefined;
|
||||
const listOptions = showFilter ? { showFilter: true, filterPlaceholder: localize('workspacePicker.filter', "Search Workspaces...") } : undefined;
|
||||
|
||||
this.actionWidgetService.show<IStoredProject>(
|
||||
'projectPicker',
|
||||
'workspacePicker',
|
||||
false,
|
||||
items,
|
||||
delegate,
|
||||
@@ -219,7 +219,7 @@ export class ProjectPicker extends Disposable {
|
||||
[],
|
||||
{
|
||||
getAriaLabel: (item) => item.label ?? '',
|
||||
getWidgetAriaLabel: () => localize('projectPicker.ariaLabel', "Project Picker"),
|
||||
getWidgetAriaLabel: () => localize('workspacePicker.ariaLabel', "Workspace Picker"),
|
||||
},
|
||||
listOptions,
|
||||
);
|
||||
@@ -229,7 +229,7 @@ export class ProjectPicker extends Disposable {
|
||||
* Programmatically set the selected project.
|
||||
* @param fireEvent Whether to fire the onDidSelectProject event. Defaults to true.
|
||||
*/
|
||||
setSelectedProject(project: SessionProject, fireEvent = true): void {
|
||||
setSelectedProject(project: SessionWorkspace, fireEvent = true): void {
|
||||
this._selectProject(project, fireEvent);
|
||||
}
|
||||
|
||||
@@ -254,7 +254,7 @@ export class ProjectPicker extends Disposable {
|
||||
}
|
||||
}
|
||||
|
||||
private _selectProject(project: SessionProject, fireEvent = true): void {
|
||||
private _selectProject(project: SessionWorkspace, fireEvent = true): void {
|
||||
this._selectedProject = project;
|
||||
const stored = this._toStored(project);
|
||||
this._addToRecents(stored);
|
||||
@@ -274,7 +274,7 @@ export class ProjectPicker extends Disposable {
|
||||
title: localize('selectFolder', "Select Folder"),
|
||||
});
|
||||
if (selected?.[0]) {
|
||||
this._selectProject(new SessionProject(selected[0]));
|
||||
this._selectProject(new SessionWorkspace(selected[0]));
|
||||
}
|
||||
} catch {
|
||||
// dialog was cancelled or failed
|
||||
@@ -285,7 +285,7 @@ export class ProjectPicker extends Disposable {
|
||||
try {
|
||||
const result: string | undefined = await this.commandService.executeCommand(OPEN_REPO_COMMAND);
|
||||
if (result) {
|
||||
this._selectProject(new SessionProject(URI.from({ scheme: GITHUB_REMOTE_FILE_SCHEME, authority: 'github', path: `/${result}/HEAD` })));
|
||||
this._selectProject(new SessionWorkspace(URI.from({ scheme: GITHUB_REMOTE_FILE_SCHEME, authority: 'github', path: `/${result}/HEAD` })));
|
||||
}
|
||||
} catch {
|
||||
// command was cancelled or failed
|
||||
@@ -386,7 +386,7 @@ export class ProjectPicker extends Disposable {
|
||||
|
||||
dom.clearNode(this._triggerElement);
|
||||
const project = this._selectedProject;
|
||||
const label = project ? this._getProjectLabel(project) : localize('pickProject', "Pick a Project");
|
||||
const label = project ? this._getProjectLabel(project) : localize('pickWorkspace', "Pick a Workspace");
|
||||
const icon = project ? (project.isFolder ? Codicon.folder : Codicon.repo) : Codicon.project;
|
||||
|
||||
dom.append(this._triggerElement, renderIcon(icon));
|
||||
@@ -395,7 +395,7 @@ export class ProjectPicker extends Disposable {
|
||||
dom.append(this._triggerElement, renderIcon(Codicon.chevronDown));
|
||||
}
|
||||
|
||||
private _getProjectLabel(project: SessionProject): string {
|
||||
private _getProjectLabel(project: SessionWorkspace): string {
|
||||
return this._getStoredProjectLabel({ uri: project.uri.toJSON() });
|
||||
}
|
||||
|
||||
@@ -408,14 +408,14 @@ export class ProjectPicker extends Disposable {
|
||||
return uri.path.substring(1).replace(/\/HEAD$/, '');
|
||||
}
|
||||
|
||||
private _toStored(project: SessionProject): IStoredProject {
|
||||
private _toStored(project: SessionWorkspace): IStoredProject {
|
||||
return {
|
||||
uri: project.uri.toJSON(),
|
||||
};
|
||||
}
|
||||
|
||||
private _fromStored(stored: IStoredProject): SessionProject {
|
||||
return new SessionProject(URI.revive(stored.uri));
|
||||
private _fromStored(stored: IStoredProject): SessionWorkspace {
|
||||
return new SessionWorkspace(URI.revive(stored.uri));
|
||||
}
|
||||
|
||||
private _projectKey(project: IStoredProject): string {
|
||||
@@ -8,7 +8,7 @@ import { IFileService } from '../../../../platform/files/common/files.js';
|
||||
import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js';
|
||||
import { registerWorkbenchContribution2, WorkbenchPhase } from '../../../../workbench/common/contributions.js';
|
||||
import { GitHubFileSystemProvider } from './githubFileSystemProvider.js';
|
||||
import { GITHUB_REMOTE_FILE_SCHEME } from '../../sessions/common/sessionProject.js';
|
||||
import { GITHUB_REMOTE_FILE_SCHEME } from '../../sessions/common/sessionWorkspace.js';
|
||||
|
||||
// --- View registration is currently disabled in favor of the "Add Context" picker.
|
||||
// The Files view will be re-enabled once we finalize the sessions auxiliary bar layout.
|
||||
|
||||
@@ -44,7 +44,7 @@ import { ITelemetryService } from '../../../../platform/telemetry/common/telemet
|
||||
import { ILogService } from '../../../../platform/log/common/log.js';
|
||||
import { ISessionsManagementService, IActiveSessionItem } from '../../sessions/browser/sessionsManagementService.js';
|
||||
import { getGitHubRemoteFileDisplayName } from './githubFileSystemProvider.js';
|
||||
import { GITHUB_REMOTE_FILE_SCHEME } from '../../sessions/common/sessionProject.js';
|
||||
import { GITHUB_REMOTE_FILE_SCHEME } from '../../sessions/common/sessionWorkspace.js';
|
||||
import { basename } from '../../../../base/common/path.js';
|
||||
import { isEqual } from '../../../../base/common/resources.js';
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ import { IAuthenticationService } from '../../../../workbench/services/authentic
|
||||
import { CancellationToken } from '../../../../base/common/cancellation.js';
|
||||
import { ILogService } from '../../../../platform/log/common/log.js';
|
||||
|
||||
import { GITHUB_REMOTE_FILE_SCHEME } from '../../sessions/common/sessionProject.js';
|
||||
import { GITHUB_REMOTE_FILE_SCHEME } from '../../sessions/common/sessionWorkspace.js';
|
||||
|
||||
/**
|
||||
* Derives a display name from a github-remote-file URI.
|
||||
|
||||
@@ -10,7 +10,7 @@ import { NullLogService, ILogService } from '../../../../../platform/log/common/
|
||||
import { TestInstantiationService } from '../../../../../platform/instantiation/test/common/instantiationServiceMock.js';
|
||||
import { GitHubService } from '../../browser/githubService.js';
|
||||
import { URI } from '../../../../../base/common/uri.js';
|
||||
import { GITHUB_REMOTE_FILE_SCHEME } from '../../../sessions/common/sessionProject.js';
|
||||
import { GITHUB_REMOTE_FILE_SCHEME } from '../../../sessions/common/sessionWorkspace.js';
|
||||
import { IActiveSessionItem } from '../../../sessions/browser/sessionsManagementService.js';
|
||||
|
||||
suite('GitHubService', () => {
|
||||
|
||||
@@ -21,12 +21,12 @@ import { IAgentSession, isAgentSession } from '../../../../workbench/contrib/cha
|
||||
import { IAgentSessionsService } from '../../../../workbench/contrib/chat/browser/agentSessions/agentSessionsService.js';
|
||||
import { ICommandService } from '../../../../platform/commands/common/commands.js';
|
||||
import { AgentSessionProviders } from '../../../../workbench/contrib/chat/browser/agentSessions/agentSessions.js';
|
||||
import { INewSession, LocalNewSession, RemoteNewSession } from '../../chat/browser/newSession.js';
|
||||
import { INewSession, CopilotCLISession, RemoteNewSession } from '../../chat/browser/newSession.js';
|
||||
import { IUriIdentityService } from '../../../../platform/uriIdentity/common/uriIdentity.js';
|
||||
import { isBuiltinChatMode } from '../../../../workbench/contrib/chat/common/chatModes.js';
|
||||
import { ILanguageModelsService } from '../../../../workbench/contrib/chat/common/languageModels.js';
|
||||
import { ILanguageModelToolsService } from '../../../../workbench/contrib/chat/common/tools/languageModelToolsService.js';
|
||||
import { GITHUB_REMOTE_FILE_SCHEME } from '../common/sessionProject.js';
|
||||
import { GITHUB_REMOTE_FILE_SCHEME } from '../common/sessionWorkspace.js';
|
||||
import { IGitHubSessionContext } from '../../github/common/types.js';
|
||||
import { ResourceSet } from '../../../../base/common/map.js';
|
||||
|
||||
@@ -267,7 +267,7 @@ export class SessionsManagementService extends Disposable implements ISessionsMa
|
||||
|
||||
let newSession: INewSession;
|
||||
if (target === AgentSessionProviders.Background) {
|
||||
newSession = this.instantiationService.createInstance(LocalNewSession, sessionResource, defaultRepoUri);
|
||||
newSession = this.instantiationService.createInstance(CopilotCLISession, sessionResource, defaultRepoUri);
|
||||
} else {
|
||||
newSession = this.instantiationService.createInstance(RemoteNewSession, sessionResource, target);
|
||||
}
|
||||
|
||||
@@ -9,10 +9,10 @@ import { IGitRepository } from '../../../../workbench/contrib/git/common/gitServ
|
||||
export const GITHUB_REMOTE_FILE_SCHEME = 'github-remote-file';
|
||||
|
||||
/**
|
||||
* Represents a project (folder or repository) for a session.
|
||||
* The project type (folder vs repo) is derived from the URI scheme.
|
||||
* Represents a workspace (folder or repository) for a session.
|
||||
* The workspace type (folder vs repo) is derived from the URI scheme.
|
||||
*/
|
||||
export class SessionProject {
|
||||
export class SessionWorkspace {
|
||||
|
||||
readonly uri: URI;
|
||||
readonly repository: IGitRepository | undefined;
|
||||
@@ -22,18 +22,18 @@ export class SessionProject {
|
||||
this.repository = repository;
|
||||
}
|
||||
|
||||
/** Whether this is a local folder project. */
|
||||
/** Whether this is a local folder workspace. */
|
||||
get isFolder(): boolean {
|
||||
return this.uri.scheme !== GITHUB_REMOTE_FILE_SCHEME;
|
||||
}
|
||||
|
||||
/** Whether this is a remote repository project. */
|
||||
/** Whether this is a remote repository workspace. */
|
||||
get isRepo(): boolean {
|
||||
return this.uri.scheme === GITHUB_REMOTE_FILE_SCHEME;
|
||||
}
|
||||
|
||||
/** Returns a new SessionProject with the repository updated. */
|
||||
withRepository(repository: IGitRepository | undefined): SessionProject {
|
||||
return new SessionProject(this.uri, repository);
|
||||
/** Returns a new SessionWorkspace with the repository updated. */
|
||||
withRepository(repository: IGitRepository | undefined): SessionWorkspace {
|
||||
return new SessionWorkspace(this.uri, repository);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user