mirror of
https://github.com/microsoft/vscode.git
synced 2026-07-02 04:27:40 +01:00
Restyle /remote success banner as native info notification (#311516)
Add a new 'info' chat content kind that mirrors the existing 'warning' path and renders via ChatErrorContentPart with ChatErrorLevel.Info, which already has blue notification styling (.chat-info-codicon). Expose this via a new ChatResponseInfoPart proposed API and stream.info() method. Use the new API in copilotcli's /remote success path so the banner renders as a native blue info notification card with a persistent 'Open on GitHub' button (vscode.open, no model roundtrip).
This commit is contained in:
+15
-17
@@ -27,7 +27,7 @@ import { DisposableStore, IDisposable, toDisposable } from '../../../../util/vs/
|
||||
import { truncate } from '../../../../util/vs/base/common/strings';
|
||||
import { ThemeIcon } from '../../../../util/vs/base/common/themables';
|
||||
import { IInstantiationService } from '../../../../util/vs/platform/instantiation/common/instantiation';
|
||||
import { ChatResponseMarkdownPart, ChatResponseThinkingProgressPart, ChatSessionStatus, ChatToolInvocationPart, EventEmitter, Uri } from '../../../../vscodeTypes';
|
||||
import { ChatResponseMarkdownPart, ChatResponseThinkingProgressPart, ChatSessionStatus, ChatToolInvocationPart, EventEmitter, MarkdownString, Uri } from '../../../../vscodeTypes';
|
||||
import { IToolsService } from '../../../tools/common/toolsService';
|
||||
import { IChatSessionMetadataStore } from '../../common/chatSessionMetadataStore';
|
||||
import { ExternalEditTracker } from '../../common/externalEditTracker';
|
||||
@@ -1191,24 +1191,22 @@ export class CopilotCLISession extends DisposableStore implements ICopilotCLISes
|
||||
const frontendUrl = `https://github.com/${nwo.owner}/${nwo.repo}/tasks/${taskId}`;
|
||||
this.logService.info(`[CopilotCLISession] MC session created, URL: ${frontendUrl}`);
|
||||
|
||||
// Show a notification popup with an "Open" button
|
||||
const vsCodeApi = require('vscode') as typeof vscode;
|
||||
const openAction = l10n.t('Open in Browser');
|
||||
vsCodeApi.window.showInformationMessage(
|
||||
l10n.t('Remote control enabled. View this session on GitHub.'),
|
||||
openAction
|
||||
).then(selection => {
|
||||
if (selection === openAction) {
|
||||
vsCodeApi.env.openExternal(vsCodeApi.Uri.parse(frontendUrl));
|
||||
}
|
||||
// Render a persistent inline info banner using the proposed
|
||||
// `stream.info()` API (blue background + blue info icon, matches
|
||||
// the native chat info notification style). The button uses
|
||||
// `vscode.open` so it opens the URL externally without invoking
|
||||
// the model, and the banner stays visible after click.
|
||||
const banner = new MarkdownString(
|
||||
`**${l10n.t('Remote control is enabled.')}** ` +
|
||||
l10n.t('You can open this session from any device.')
|
||||
);
|
||||
this._stream?.info(banner);
|
||||
this._stream?.button({
|
||||
command: 'vscode.open',
|
||||
arguments: [Uri.parse(frontendUrl)],
|
||||
title: l10n.t('Open on GitHub'),
|
||||
});
|
||||
|
||||
// Also show the link inline in the chat stream
|
||||
this._stream?.markdown(l10n.t(
|
||||
'Remote control enabled. Open this session from any device:\n\n[{0}]({1})',
|
||||
frontendUrl, frontendUrl
|
||||
));
|
||||
|
||||
// Step 9: Start continuous event exporter and command poller
|
||||
this._startMcEventExporter();
|
||||
this._startMcCommandPoller();
|
||||
|
||||
@@ -2132,6 +2132,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
||||
ChatResponseCodeCitationPart: extHostTypes.ChatResponseCodeCitationPart,
|
||||
ChatResponseCodeblockUriPart: extHostTypes.ChatResponseCodeblockUriPart,
|
||||
ChatResponseWarningPart: extHostTypes.ChatResponseWarningPart,
|
||||
ChatResponseInfoPart: extHostTypes.ChatResponseInfoPart,
|
||||
ChatResponseTextEditPart: extHostTypes.ChatResponseTextEditPart,
|
||||
ChatResponseNotebookEditPart: extHostTypes.ChatResponseNotebookEditPart,
|
||||
ChatResponseWorkspaceEditPart: extHostTypes.ChatResponseWorkspaceEditPart,
|
||||
|
||||
@@ -224,6 +224,14 @@ export class ChatAgentResponseStream {
|
||||
_report(dto);
|
||||
return this;
|
||||
},
|
||||
info(value) {
|
||||
throwIfDone(this.progress);
|
||||
checkProposedApiEnabled(that._extension, 'chatParticipantAdditions');
|
||||
const part = new extHostTypes.ChatResponseInfoPart(value);
|
||||
const dto = typeConvert.ChatResponseInfoPart.from(part);
|
||||
_report(dto);
|
||||
return this;
|
||||
},
|
||||
reference(value, iconPath) {
|
||||
return this.reference2(value, iconPath);
|
||||
},
|
||||
|
||||
@@ -43,7 +43,7 @@ import { DEFAULT_EDITOR_ASSOCIATION, SaveReason } from '../../common/editor.js';
|
||||
import { IViewBadge } from '../../common/views.js';
|
||||
import { IChatAgentRequest, IChatAgentResult } from '../../contrib/chat/common/participants/chatAgents.js';
|
||||
import { IChatRequestModeInstructions } from '../../contrib/chat/common/model/chatModel.js';
|
||||
import { IChatAgentMarkdownContentWithVulnerability, IChatCodeCitation, IChatCommandButton, IChatConfirmation, IChatContentInlineReference, IChatContentReference, IChatExtensionsContent, IChatExternalToolInvocationUpdate, IChatFollowup, IChatHookPart, IChatMarkdownContent, IChatMoveMessage, IChatMultiDiffDataSerialized, IChatProgressMessage, IChatPullRequestContent, IChatQuestionCarousel, IChatResponseCodeblockUriPart, IChatTaskDto, IChatTaskResult, IChatTerminalToolInvocationData, IChatTextEdit, IChatThinkingPart, IChatToolInvocationSerialized, IChatTreeData, IChatUserActionEvent, IChatWarningMessage, IChatWorkspaceEdit } from '../../contrib/chat/common/chatService/chatService.js';
|
||||
import { IChatAgentMarkdownContentWithVulnerability, IChatCodeCitation, IChatCommandButton, IChatConfirmation, IChatContentInlineReference, IChatContentReference, IChatExtensionsContent, IChatExternalToolInvocationUpdate, IChatFollowup, IChatHookPart, IChatMarkdownContent, IChatMoveMessage, IChatMultiDiffDataSerialized, IChatProgressMessage, IChatPullRequestContent, IChatQuestionCarousel, IChatResponseCodeblockUriPart, IChatTaskDto, IChatTaskResult, IChatTerminalToolInvocationData, IChatTextEdit, IChatThinkingPart, IChatToolInvocationSerialized, IChatTreeData, IChatUserActionEvent, IChatWarningMessage, IChatInfoMessage, IChatWorkspaceEdit } from '../../contrib/chat/common/chatService/chatService.js';
|
||||
import { LocalChatSessionUri } from '../../contrib/chat/common/model/chatUri.js';
|
||||
import { ChatRequestToolReferenceEntry, IChatRequestVariableEntry, isImageVariableEntry, isPromptFileVariableEntry, isPromptTextVariableEntry } from '../../contrib/chat/common/attachments/chatVariableEntries.js';
|
||||
import { ChatSessionStatus, IChatSessionItem } from '../../contrib/chat/common/chatSessionsService.js';
|
||||
@@ -2852,6 +2852,18 @@ export namespace ChatResponseWarningPart {
|
||||
}
|
||||
}
|
||||
|
||||
export namespace ChatResponseInfoPart {
|
||||
export function from(part: vscode.ChatResponseInfoPart): Dto<IChatInfoMessage> {
|
||||
return {
|
||||
kind: 'info',
|
||||
content: MarkdownString.from(part.value)
|
||||
};
|
||||
}
|
||||
export function to(part: Dto<IChatInfoMessage>): vscode.ChatResponseInfoPart {
|
||||
return new types.ChatResponseInfoPart(part.content.value);
|
||||
}
|
||||
}
|
||||
|
||||
export namespace ChatResponseExtensionsPart {
|
||||
export function from(part: vscode.ChatResponseExtensionsPart): Dto<IChatExtensionsContent> {
|
||||
return {
|
||||
@@ -3354,6 +3366,8 @@ export namespace ChatResponsePart {
|
||||
return ChatResponseCodeblockUriPart.from(part);
|
||||
} else if (part instanceof types.ChatResponseWarningPart) {
|
||||
return ChatResponseWarningPart.from(part);
|
||||
} else if (part instanceof types.ChatResponseInfoPart) {
|
||||
return ChatResponseInfoPart.from(part);
|
||||
} else if (part instanceof types.ChatResponseConfirmationPart) {
|
||||
return ChatResponseConfirmationPart.from(part);
|
||||
} else if (part instanceof types.ChatResponseQuestionCarouselPart) {
|
||||
|
||||
@@ -3273,6 +3273,17 @@ export class ChatResponseWarningPart {
|
||||
}
|
||||
}
|
||||
|
||||
export class ChatResponseInfoPart {
|
||||
value: vscode.MarkdownString;
|
||||
constructor(value: string | vscode.MarkdownString) {
|
||||
if (typeof value !== 'string' && value.isTrusted === true) {
|
||||
throw new Error('The boolean form of MarkdownString.isTrusted is NOT supported for chat participants.');
|
||||
}
|
||||
|
||||
this.value = typeof value === 'string' ? new MarkdownString(value) : value;
|
||||
}
|
||||
}
|
||||
|
||||
export class ChatResponseCommandButtonPart {
|
||||
value: vscode.Command;
|
||||
constructor(value: vscode.Command) {
|
||||
|
||||
@@ -2194,6 +2194,8 @@ export class ChatListItemRenderer extends Disposable implements ITreeRenderer<Ch
|
||||
return this.renderConfirmation(context, content, templateData);
|
||||
} else if (content.kind === 'warning') {
|
||||
return this.instantiationService.createInstance(ChatErrorContentPart, ChatErrorLevel.Warning, content.content, content, this.chatContentMarkdownRenderer);
|
||||
} else if (content.kind === 'info') {
|
||||
return this.instantiationService.createInstance(ChatErrorContentPart, ChatErrorLevel.Info, content.content, content, this.chatContentMarkdownRenderer);
|
||||
} else if (content.kind === 'hook') {
|
||||
return this.renderHookPart(content, context, templateData);
|
||||
} else if (content.kind === 'markdownContent') {
|
||||
|
||||
@@ -289,6 +289,11 @@ export interface IChatWarningMessage {
|
||||
kind: 'warning';
|
||||
}
|
||||
|
||||
export interface IChatInfoMessage {
|
||||
content: IMarkdownString;
|
||||
kind: 'info';
|
||||
}
|
||||
|
||||
export interface IChatAgentVulnerabilityDetails {
|
||||
title: string;
|
||||
description: string;
|
||||
@@ -1105,6 +1110,7 @@ export type IChatProgress =
|
||||
| IChatTaskResult
|
||||
| IChatCommandButton
|
||||
| IChatWarningMessage
|
||||
| IChatInfoMessage
|
||||
| IChatTextEdit
|
||||
| IChatNotebookEdit
|
||||
| IChatWorkspaceEdit
|
||||
|
||||
@@ -30,7 +30,7 @@ import { CellUri, ICellEditOperation } from '../../../notebook/common/notebookCo
|
||||
import { ChatRequestToolReferenceEntry, IChatRequestVariableEntry, isImplicitVariableEntry, isStringImplicitContextValue, isStringVariableEntry } from '../attachments/chatVariableEntries.js';
|
||||
import { migrateLegacyTerminalToolSpecificData } from '../chat.js';
|
||||
import { ChatPerfMark, markChat } from '../chatPerf.js';
|
||||
import { ChatAgentVoteDirection, ChatRequestQueueKind, ChatResponseClearToPreviousToolInvocationReason, ElicitationState, IChatAgentMarkdownContentWithVulnerability, IChatClearToPreviousToolInvocation, IChatCodeCitation, IChatCommandButton, IChatConfirmation, IChatContentInlineReference, IChatContentReference, IChatDisabledClaudeHooksPart, IChatEditingSessionAction, IChatElicitationRequest, IChatElicitationRequestSerialized, IChatExternalToolInvocationUpdate, IChatExtensionsContent, IChatFollowup, IChatHookPart, IChatLocationData, IChatMarkdownContent, IChatMcpServersStarting, IChatMcpServersStartingSerialized, IChatModelReference, IChatMultiDiffData, IChatMultiDiffDataSerialized, IChatNotebookEdit, IChatProgress, IChatPlanReview, IChatProgressMessage, IChatPullRequestContent, IChatQuestionCarousel, IChatResponseCodeblockUriPart, IChatResponseProgressFileTreeData, IChatSendRequestOptions, IChatService, IChatSessionTiming, IChatTask, IChatTaskSerialized, IChatTextEdit, IChatThinkingPart, IChatToolInvocation, IChatToolInvocationSerialized, IChatTreeData, IChatUndoStop, IChatUsage, IChatUsedContext, IChatWarningMessage, IChatWorkspaceEdit, ResponseModelState, ToolConfirmKind, isIUsedContext } from '../chatService/chatService.js';
|
||||
import { ChatAgentVoteDirection, ChatRequestQueueKind, ChatResponseClearToPreviousToolInvocationReason, ElicitationState, IChatAgentMarkdownContentWithVulnerability, IChatClearToPreviousToolInvocation, IChatCodeCitation, IChatCommandButton, IChatConfirmation, IChatContentInlineReference, IChatContentReference, IChatDisabledClaudeHooksPart, IChatEditingSessionAction, IChatElicitationRequest, IChatElicitationRequestSerialized, IChatExternalToolInvocationUpdate, IChatExtensionsContent, IChatFollowup, IChatHookPart, IChatLocationData, IChatMarkdownContent, IChatMcpServersStarting, IChatMcpServersStartingSerialized, IChatModelReference, IChatMultiDiffData, IChatMultiDiffDataSerialized, IChatNotebookEdit, IChatProgress, IChatPlanReview, IChatProgressMessage, IChatPullRequestContent, IChatQuestionCarousel, IChatResponseCodeblockUriPart, IChatResponseProgressFileTreeData, IChatSendRequestOptions, IChatService, IChatSessionTiming, IChatTask, IChatTaskSerialized, IChatTextEdit, IChatThinkingPart, IChatToolInvocation, IChatToolInvocationSerialized, IChatTreeData, IChatUndoStop, IChatUsage, IChatUsedContext, IChatWarningMessage, IChatInfoMessage, IChatWorkspaceEdit, ResponseModelState, ToolConfirmKind, isIUsedContext } from '../chatService/chatService.js';
|
||||
import { ChatAgentLocation, ChatModeKind, ChatPermissionLevel } from '../constants.js';
|
||||
import { ChatToolInvocation } from './chatProgressTypes/chatToolInvocation.js';
|
||||
import { ToolDataSource, IToolData } from '../tools/languageModelToolsService.js';
|
||||
@@ -189,6 +189,7 @@ export type IChatProgressHistoryResponseContent =
|
||||
| IChatProgressMessage
|
||||
| IChatCommandButton
|
||||
| IChatWarningMessage
|
||||
| IChatInfoMessage
|
||||
| IChatTask
|
||||
| IChatTaskSerialized
|
||||
| IChatTextEditGroup
|
||||
@@ -637,6 +638,7 @@ class AbstractResponse implements IResponse {
|
||||
case 'progressTask':
|
||||
case 'progressTaskSerialized':
|
||||
case 'warning':
|
||||
case 'info':
|
||||
segment = { text: part.content.value };
|
||||
break;
|
||||
default:
|
||||
|
||||
@@ -81,6 +81,7 @@ const responsePartSchema = Adapt.v<IChatProgressResponseContent, SerializedChatR
|
||||
case 'planReview':
|
||||
case 'undoStop':
|
||||
case 'warning':
|
||||
case 'info':
|
||||
case 'treeData':
|
||||
case 'workspaceEdit':
|
||||
case 'disabledClaudeHooks':
|
||||
|
||||
@@ -453,6 +453,11 @@ declare module 'vscode' {
|
||||
constructor(value: string | MarkdownString);
|
||||
}
|
||||
|
||||
export class ChatResponseInfoPart {
|
||||
value: MarkdownString;
|
||||
constructor(value: string | MarkdownString);
|
||||
}
|
||||
|
||||
export class ChatResponseProgressPart2 extends ChatResponseProgressPart {
|
||||
value: string;
|
||||
task?: (progress: Progress<ChatResponseWarningPart | ChatResponseReferencePart>) => Thenable<string | void>;
|
||||
@@ -633,6 +638,15 @@ declare module 'vscode' {
|
||||
*/
|
||||
warning(message: string | MarkdownString): void;
|
||||
|
||||
/**
|
||||
* Push an info banner to this stream. Short-hand for
|
||||
* `push(new ChatResponseInfoPart(message))`.
|
||||
*
|
||||
* @param message An informational message
|
||||
* @returns This stream.
|
||||
*/
|
||||
info(message: string | MarkdownString): void;
|
||||
|
||||
reference(value: Uri | Location | { variableName: string; value?: Uri | Location }, iconPath?: Uri | ThemeIcon | { light: Uri; dark: Uri }): void;
|
||||
|
||||
reference2(value: Uri | Location | string | { variableName: string; value?: Uri | Location }, iconPath?: Uri | ThemeIcon | { light: Uri; dark: Uri }, options?: { status?: { description: string; kind: ChatResponseReferencePartStatusKind } }): void;
|
||||
|
||||
Reference in New Issue
Block a user