mirror of
https://github.com/microsoft/vscode.git
synced 2025-12-19 17:58:39 +00:00
Add delegate button on untitled prompt files (#278811)
* prototype * add location * add in known issue as cast for some reason * tidy
This commit is contained in:
@@ -195,3 +195,10 @@
|
||||
opacity: 0.7;
|
||||
margin-left: 0.5em;
|
||||
}
|
||||
|
||||
.action-widget-delegate-label {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
@@ -4,7 +4,8 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Codicon } from '../../../../../base/common/codicons.js';
|
||||
import { IDisposable } from '../../../../../base/common/lifecycle.js';
|
||||
import { Disposable, IDisposable, markAsSingleton } from '../../../../../base/common/lifecycle.js';
|
||||
import { Schemas } from '../../../../../base/common/network.js';
|
||||
import { basename } from '../../../../../base/common/resources.js';
|
||||
import { ThemeIcon } from '../../../../../base/common/themables.js';
|
||||
import { URI } from '../../../../../base/common/uri.js';
|
||||
@@ -12,6 +13,7 @@ import { ServicesAccessor } from '../../../../../editor/browser/editorExtensions
|
||||
import { isITextModel } from '../../../../../editor/common/model.js';
|
||||
import { localize, localize2 } from '../../../../../nls.js';
|
||||
import { ActionWidgetDropdownActionViewItem } from '../../../../../platform/actions/browser/actionWidgetDropdownActionViewItem.js';
|
||||
import { IActionViewItemService } from '../../../../../platform/actions/browser/actionViewItemService.js';
|
||||
import { Action2, MenuId, MenuItemAction } from '../../../../../platform/actions/common/actions.js';
|
||||
import { IActionWidgetService } from '../../../../../platform/actionWidget/browser/actionWidget.js';
|
||||
import { IActionWidgetDropdownAction, IActionWidgetDropdownActionProvider } from '../../../../../platform/actionWidget/browser/actionWidgetDropdown.js';
|
||||
@@ -20,17 +22,28 @@ import { ContextKeyExpr, IContextKeyService } from '../../../../../platform/cont
|
||||
import { IInstantiationService } from '../../../../../platform/instantiation/common/instantiation.js';
|
||||
import { IKeybindingService } from '../../../../../platform/keybinding/common/keybinding.js';
|
||||
import { IOpenerService } from '../../../../../platform/opener/common/opener.js';
|
||||
import { IWorkbenchContribution } from '../../../../common/contributions.js';
|
||||
import { ResourceContextKey } from '../../../../common/contextkeys.js';
|
||||
import { IEditorService } from '../../../../services/editor/common/editorService.js';
|
||||
import { IChatAgentService } from '../../common/chatAgents.js';
|
||||
import { ChatContextKeys } from '../../common/chatContextKeys.js';
|
||||
import { chatEditingWidgetFileStateContextKey, ModifiedFileEntryState } from '../../common/chatEditingService.js';
|
||||
import { ChatModel } from '../../common/chatModel.js';
|
||||
import { ChatRequestParser } from '../../common/chatRequestParser.js';
|
||||
import { IChatService } from '../../common/chatService.js';
|
||||
import { IChatSessionsExtensionPoint, IChatSessionsService } from '../../common/chatSessionsService.js';
|
||||
import { ChatAgentLocation } from '../../common/constants.js';
|
||||
import { PROMPT_LANGUAGE_ID } from '../../common/promptSyntax/promptTypes.js';
|
||||
import { AgentSessionProviders, getAgentSessionProviderIcon, getAgentSessionProviderName } from '../agentSessions/agentSessions.js';
|
||||
import { IChatWidgetService } from '../chat.js';
|
||||
import { CHAT_SETUP_ACTION_ID } from './chatActions.js';
|
||||
import { CancellationToken } from '../../../../../base/common/cancellation.js';
|
||||
import { IChatRequestVariableEntry } from '../../common/chatVariableEntries.js';
|
||||
|
||||
export const enum ActionLocation {
|
||||
ChatWidget = 'chatWidget',
|
||||
Editor = 'editor'
|
||||
}
|
||||
|
||||
export class ContinueChatInSessionAction extends Action2 {
|
||||
|
||||
@@ -46,12 +59,22 @@ export class ContinueChatInSessionAction extends Action2 {
|
||||
ChatContextKeys.requestInProgress.negate(),
|
||||
ChatContextKeys.remoteJobCreating.negate(),
|
||||
),
|
||||
menu: {
|
||||
menu: [{
|
||||
id: MenuId.ChatExecute,
|
||||
group: 'navigation',
|
||||
order: 3.4,
|
||||
when: ChatContextKeys.lockedToCodingAgent.negate(),
|
||||
},
|
||||
{
|
||||
id: MenuId.EditorContent,
|
||||
group: 'continueIn',
|
||||
when: ContextKeyExpr.and(
|
||||
ContextKeyExpr.equals(ResourceContextKey.Scheme.key, Schemas.untitled),
|
||||
ContextKeyExpr.equals(ResourceContextKey.LangId.key, PROMPT_LANGUAGE_ID),
|
||||
ContextKeyExpr.notEquals(chatEditingWidgetFileStateContextKey.key, ModifiedFileEntryState.Modified),
|
||||
),
|
||||
}
|
||||
]
|
||||
});
|
||||
}
|
||||
|
||||
@@ -59,10 +82,10 @@ export class ContinueChatInSessionAction extends Action2 {
|
||||
// Handled by a custom action item
|
||||
}
|
||||
}
|
||||
|
||||
export class ChatContinueInSessionActionItem extends ActionWidgetDropdownActionViewItem {
|
||||
constructor(
|
||||
action: MenuItemAction,
|
||||
private readonly location: ActionLocation,
|
||||
@IActionWidgetService actionWidgetService: IActionWidgetService,
|
||||
@IContextKeyService private readonly contextKeyService: IContextKeyService,
|
||||
@IKeybindingService keybindingService: IKeybindingService,
|
||||
@@ -71,12 +94,12 @@ export class ChatContinueInSessionActionItem extends ActionWidgetDropdownActionV
|
||||
@IOpenerService openerService: IOpenerService
|
||||
) {
|
||||
super(action, {
|
||||
actionProvider: ChatContinueInSessionActionItem.actionProvider(chatSessionsService, instantiationService),
|
||||
actionProvider: ChatContinueInSessionActionItem.actionProvider(chatSessionsService, instantiationService, location),
|
||||
actionBarActions: ChatContinueInSessionActionItem.getActionBarActions(openerService)
|
||||
}, actionWidgetService, keybindingService, contextKeyService);
|
||||
}
|
||||
|
||||
private static getActionBarActions(openerService: IOpenerService) {
|
||||
protected static getActionBarActions(openerService: IOpenerService) {
|
||||
const learnMoreUrl = 'https://aka.ms/vscode-continue-chat-in';
|
||||
return [{
|
||||
id: 'workbench.action.chat.continueChatInSession.learnMore',
|
||||
@@ -90,7 +113,7 @@ export class ChatContinueInSessionActionItem extends ActionWidgetDropdownActionV
|
||||
}];
|
||||
}
|
||||
|
||||
private static actionProvider(chatSessionsService: IChatSessionsService, instantiationService: IInstantiationService): IActionWidgetDropdownActionProvider {
|
||||
private static actionProvider(chatSessionsService: IChatSessionsService, instantiationService: IInstantiationService, location: ActionLocation): IActionWidgetDropdownActionProvider {
|
||||
return {
|
||||
getActions: () => {
|
||||
const actions: IActionWidgetDropdownAction[] = [];
|
||||
@@ -99,13 +122,13 @@ export class ChatContinueInSessionActionItem extends ActionWidgetDropdownActionV
|
||||
// Continue in Background
|
||||
const backgroundContrib = contributions.find(contrib => contrib.type === AgentSessionProviders.Background);
|
||||
if (backgroundContrib && backgroundContrib.canDelegate !== false) {
|
||||
actions.push(this.toAction(AgentSessionProviders.Background, backgroundContrib, instantiationService));
|
||||
actions.push(this.toAction(AgentSessionProviders.Background, backgroundContrib, instantiationService, location));
|
||||
}
|
||||
|
||||
// Continue in Cloud
|
||||
const cloudContrib = contributions.find(contrib => contrib.type === AgentSessionProviders.Cloud);
|
||||
if (cloudContrib && cloudContrib.canDelegate !== false) {
|
||||
actions.push(this.toAction(AgentSessionProviders.Cloud, cloudContrib, instantiationService));
|
||||
actions.push(this.toAction(AgentSessionProviders.Cloud, cloudContrib, instantiationService, location));
|
||||
}
|
||||
|
||||
// Offer actions to enter setup if we have no contributions
|
||||
@@ -119,7 +142,7 @@ export class ChatContinueInSessionActionItem extends ActionWidgetDropdownActionV
|
||||
};
|
||||
}
|
||||
|
||||
private static toAction(provider: AgentSessionProviders, contrib: IChatSessionsExtensionPoint, instantiationService: IInstantiationService): IActionWidgetDropdownAction {
|
||||
private static toAction(provider: AgentSessionProviders, contrib: IChatSessionsExtensionPoint, instantiationService: IInstantiationService, location: ActionLocation): IActionWidgetDropdownAction {
|
||||
return {
|
||||
id: contrib.type,
|
||||
enabled: true,
|
||||
@@ -128,7 +151,12 @@ export class ChatContinueInSessionActionItem extends ActionWidgetDropdownActionV
|
||||
description: `@${contrib.name}`,
|
||||
label: localize('continueSessionIn', "Continue in {0}", getAgentSessionProviderName(provider)),
|
||||
tooltip: contrib.displayName,
|
||||
run: () => instantiationService.invokeFunction(accessor => new CreateRemoteAgentJobAction().run(accessor, contrib))
|
||||
run: () => instantiationService.invokeFunction(accessor => {
|
||||
if (location === ActionLocation.Editor) {
|
||||
return new CreateRemoteAgentJobFromEditorAction().run(accessor, contrib);
|
||||
}
|
||||
return new CreateRemoteAgentJobAction().run(accessor, contrib);
|
||||
})
|
||||
};
|
||||
}
|
||||
|
||||
@@ -148,10 +176,25 @@ export class ChatContinueInSessionActionItem extends ActionWidgetDropdownActionV
|
||||
}
|
||||
|
||||
protected override renderLabel(element: HTMLElement): IDisposable | null {
|
||||
const icon = this.contextKeyService.contextMatchesRules(ChatContextKeys.remoteJobCreating) ? Codicon.sync : Codicon.forward;
|
||||
element.classList.add(...ThemeIcon.asClassNameArray(icon));
|
||||
if (this.location === ActionLocation.Editor) {
|
||||
const container = document.createElement('span');
|
||||
container.classList.add('action-widget-delegate-label');
|
||||
|
||||
return super.renderLabel(element);
|
||||
const iconSpan = document.createElement('span');
|
||||
iconSpan.classList.add(...ThemeIcon.asClassNameArray(Codicon.forward));
|
||||
container.appendChild(iconSpan);
|
||||
|
||||
const textSpan = document.createElement('span');
|
||||
textSpan.textContent = localize('delegate', "Delegate to...");
|
||||
container.appendChild(textSpan);
|
||||
|
||||
element.appendChild(container);
|
||||
return null;
|
||||
} else {
|
||||
const icon = this.contextKeyService.contextMatchesRules(ChatContextKeys.remoteJobCreating) ? Codicon.sync : Codicon.forward;
|
||||
element.classList.add(...ThemeIcon.asClassNameArray(icon));
|
||||
return super.renderLabel(element);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -249,3 +292,67 @@ class CreateRemoteAgentJobAction {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class CreateRemoteAgentJobFromEditorAction {
|
||||
constructor() { }
|
||||
|
||||
async run(accessor: ServicesAccessor, continuationTarget: IChatSessionsExtensionPoint) {
|
||||
|
||||
try {
|
||||
const chatService = accessor.get(IChatService);
|
||||
const continuationTargetType = continuationTarget.type;
|
||||
const editorService = accessor.get(IEditorService);
|
||||
const activeEditor = editorService.activeTextEditorControl;
|
||||
const editorService2 = accessor.get(IEditorService);
|
||||
|
||||
if (!activeEditor) {
|
||||
return;
|
||||
}
|
||||
const model = activeEditor.getModel();
|
||||
if (!model || !isITextModel(model)) {
|
||||
return;
|
||||
}
|
||||
const fileUri = model.uri as URI;
|
||||
const chatModelReference = chatService.startSession(ChatAgentLocation.Chat, CancellationToken.None, {});
|
||||
const { sessionResource } = chatModelReference.object;
|
||||
if (!sessionResource) {
|
||||
return;
|
||||
}
|
||||
await editorService2.openEditor({ resource: sessionResource }, undefined);
|
||||
const attachedContext: IChatRequestVariableEntry[] = [{
|
||||
kind: 'file',
|
||||
id: 'vscode.implicit.selection',
|
||||
name: basename(fileUri),
|
||||
value: {
|
||||
uri: fileUri
|
||||
},
|
||||
}];
|
||||
await chatService.sendRequest(sessionResource, `Implement this.`, {
|
||||
agentIdSilent: continuationTargetType,
|
||||
attachedContext
|
||||
});
|
||||
} catch (e) {
|
||||
console.error('Error creating remote agent job from editor', e);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class ContinueChatInSessionActionRendering extends Disposable implements IWorkbenchContribution {
|
||||
|
||||
static readonly ID = 'chat.continueChatInSessionActionRendering';
|
||||
|
||||
constructor(
|
||||
@IActionViewItemService actionViewItemService: IActionViewItemService,
|
||||
@IInstantiationService instantiationService: IInstantiationService,
|
||||
) {
|
||||
super();
|
||||
const disposable = actionViewItemService.register(MenuId.EditorContent, ContinueChatInSessionAction.ID, (action, options, instantiationService2) => {
|
||||
if (!(action instanceof MenuItemAction)) {
|
||||
return undefined;
|
||||
}
|
||||
return instantiationService.createInstance(ChatContinueInSessionActionItem, action, ActionLocation.Editor);
|
||||
});
|
||||
markAsSingleton(disposable);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,6 +68,7 @@ import { ACTION_ID_NEW_CHAT, CopilotTitleBarMenuRendering, ModeOpenChatGlobalAct
|
||||
import { CodeBlockActionRendering, registerChatCodeBlockActions, registerChatCodeCompareBlockActions } from './actions/chatCodeblockActions.js';
|
||||
import { ChatContextContributions } from './actions/chatContext.js';
|
||||
import { registerChatContextActions } from './actions/chatContextActions.js';
|
||||
import { ContinueChatInSessionActionRendering } from './actions/chatContinueInAction.js';
|
||||
import { registerChatCopyActions } from './actions/chatCopyActions.js';
|
||||
import { registerChatDeveloperActions } from './actions/chatDeveloperActions.js';
|
||||
import { ChatSubmitAction, registerChatExecuteActions } from './actions/chatExecuteActions.js';
|
||||
@@ -1123,6 +1124,7 @@ registerWorkbenchContribution2(ChatPromptFilesExtensionPointHandler.ID, ChatProm
|
||||
registerWorkbenchContribution2(ChatCompatibilityNotifier.ID, ChatCompatibilityNotifier, WorkbenchPhase.Eventually);
|
||||
registerWorkbenchContribution2(CopilotTitleBarMenuRendering.ID, CopilotTitleBarMenuRendering, WorkbenchPhase.BlockRestore);
|
||||
registerWorkbenchContribution2(CodeBlockActionRendering.ID, CodeBlockActionRendering, WorkbenchPhase.BlockRestore);
|
||||
registerWorkbenchContribution2(ContinueChatInSessionActionRendering.ID, ContinueChatInSessionActionRendering, WorkbenchPhase.BlockRestore);
|
||||
registerWorkbenchContribution2(ChatImplicitContextContribution.ID, ChatImplicitContextContribution, WorkbenchPhase.Eventually);
|
||||
registerWorkbenchContribution2(ChatRelatedFilesContribution.ID, ChatRelatedFilesContribution, WorkbenchPhase.Eventually);
|
||||
registerWorkbenchContribution2(ChatViewsWelcomeHandler.ID, ChatViewsWelcomeHandler, WorkbenchPhase.BlockStartup);
|
||||
|
||||
@@ -85,7 +85,7 @@ import { ChatHistoryNavigator } from '../common/chatWidgetHistoryService.js';
|
||||
import { ChatAgentLocation, ChatConfiguration, ChatModeKind, validateChatMode } from '../common/constants.js';
|
||||
import { ILanguageModelChatMetadata, ILanguageModelChatMetadataAndIdentifier, ILanguageModelsService } from '../common/languageModels.js';
|
||||
import { ILanguageModelToolsService } from '../common/languageModelToolsService.js';
|
||||
import { ChatContinueInSessionActionItem, ContinueChatInSessionAction } from './actions/chatContinueInAction.js';
|
||||
import { ActionLocation, ChatContinueInSessionActionItem, ContinueChatInSessionAction } from './actions/chatContinueInAction.js';
|
||||
import { ChatOpenModelPickerActionId, ChatSessionPrimaryPickerAction, ChatSubmitAction, IChatExecuteActionContext, OpenModePickerAction } from './actions/chatExecuteActions.js';
|
||||
import { ImplicitContextAttachmentWidget } from './attachments/implicitContextAttachment.js';
|
||||
import { IChatWidget } from './chat.js';
|
||||
@@ -1538,7 +1538,7 @@ export class ChatInputPart extends Disposable implements IHistoryNavigationWidge
|
||||
hiddenItemStrategy: HiddenItemStrategy.NoHide,
|
||||
actionViewItemProvider: (action, options) => {
|
||||
if (action.id === ContinueChatInSessionAction.ID && action instanceof MenuItemAction) {
|
||||
return this.instantiationService.createInstance(ChatContinueInSessionActionItem, action);
|
||||
return this.instantiationService.createInstance(ChatContinueInSessionActionItem, action, ActionLocation.ChatWidget);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user