From 4bd8dd24f5ddfb5df03d01fc6b5a9bb9c08801fd Mon Sep 17 00:00:00 2001
From: amitjoshi
- + \ No newline at end of file diff --git a/package.json b/package.json index 50bb54b8..0c1f7a8b 100644 --- a/package.json +++ b/package.json @@ -119,7 +119,8 @@ "fullName": "Copilot for Power Pages", "description": "Copilot for Power Pages", "isSticky": true, - "when": "config.powerPlatform.experimental.powerPagesInGitHubCopilotChat" + "when": "config.powerPlatform.experimental.powerPagesInGitHubCopilotChat", + "sampleRequest": "How can you help with coding for my website?" } ], "problemMatchers": [ diff --git a/src/common/chat-participants/powerpages/PowerPagesChatParticipant.ts b/src/common/chat-participants/powerpages/PowerPagesChatParticipant.ts index 1c1f3c5c..55da4726 100644 --- a/src/common/chat-participants/powerpages/PowerPagesChatParticipant.ts +++ b/src/common/chat-participants/powerpages/PowerPagesChatParticipant.ts @@ -11,9 +11,9 @@ import { sendApiRequest } from '../../copilot/IntelligenceApiService'; import { PacWrapper } from '../../../client/pac/PacWrapper'; import { intelligenceAPIAuthentication } from '../../services/AuthenticationProvider'; import { ActiveOrgOutput } from '../../../client/pac/PacTypes'; -import { AUTHENTICATION_FAILED_MSG, COPILOT_NOT_AVAILABLE_MSG, DISCLAIMER_MESSAGE, NO_PROMPT_MESSAGE, PAC_AUTH_NOT_FOUND, POWERPAGES_CHAT_PARTICIPANT_ID, RESPONSE_AWAITED_MSG, SKIP_CODES, VSCODE_EXTENSION_GITHUB_POWER_PAGES_AGENT_INVOKED, VSCODE_EXTENSION_GITHUB_POWER_PAGES_AGENT_ORG_DETAILS, VSCODE_EXTENSION_GITHUB_POWER_PAGES_AGENT_ORG_DETAILS_NOT_FOUND, VSCODE_EXTENSION_GITHUB_POWER_PAGES_AGENT_SCENARIO } from './PowerPagesChatParticipantConstants'; +import { AUTHENTICATION_FAILED_MSG, COPILOT_NOT_AVAILABLE_MSG, DISCLAIMER_MESSAGE, NO_PROMPT_MESSAGE, PAC_AUTH_NOT_FOUND, POWERPAGES_CHAT_PARTICIPANT_ID, RESPONSE_AWAITED_MSG, SKIP_CODES, STATER_PROMPTS, VSCODE_EXTENSION_GITHUB_POWER_PAGES_AGENT_INVOKED, VSCODE_EXTENSION_GITHUB_POWER_PAGES_AGENT_ORG_DETAILS, VSCODE_EXTENSION_GITHUB_POWER_PAGES_AGENT_ORG_DETAILS_NOT_FOUND, VSCODE_EXTENSION_GITHUB_POWER_PAGES_AGENT_SCENARIO, WELCOME_MESSAGE, WELCOME_PROMPT } from './PowerPagesChatParticipantConstants'; import { ORG_DETAILS_KEY, handleOrgChangeSuccess, initializeOrgDetails } from '../../utilities/OrgHandlerUtils'; -import { getComponentInfo, getEndpoint } from './PowerPagesChatParticipantUtils'; +import { getComponentInfo, getEndpoint, provideChatParticipantFollowups } from './PowerPagesChatParticipantUtils'; import { checkCopilotAvailability, getActiveEditorContent } from '../../utilities/Utils'; import { IIntelligenceAPIEndpointInformation } from '../../services/Interfaces'; import { v4 as uuidv4 } from 'uuid'; @@ -42,6 +42,10 @@ export class PowerPagesChatParticipant { //TODO: Check the icon image this.chatParticipant.iconPath = vscode.Uri.joinPath(context.extensionUri, 'src', 'common', 'chat-participants', 'powerpages', 'assets', 'copilot.png'); + this.chatParticipant.followupProvider = { + provideFollowups: provideChatParticipantFollowups + }; + this.powerPagesAgentSessionId = uuidv4(); this.telemetry = telemetry; @@ -57,7 +61,6 @@ export class PowerPagesChatParticipant { this._disposables.push(orgChangeErrorEvent(async () => { this.extensionContext.globalState.update(ORG_DETAILS_KEY, { orgID: undefined, orgUrl: undefined }); })); - } public static getInstance(context: vscode.ExtensionContext, telemetry: ITelemetry | TelemetryReporter, pacWrapper?: PacWrapper) { @@ -124,25 +127,32 @@ export class PowerPagesChatParticipant { }; } - if (request.command) { - //TODO: Handle command scenarios + const userPrompt = request.prompt; - } else { + if (userPrompt === WELCOME_PROMPT) { + stream.markdown(WELCOME_MESSAGE); + return{ + metadata: { + command: STATER_PROMPTS + } + } + } - const userPrompt = request.prompt; + if (!userPrompt) { - if (!userPrompt) { + stream.markdown(NO_PROMPT_MESSAGE); - //TODO: String approval is required - stream.markdown(NO_PROMPT_MESSAGE); + return { + metadata: { + command: STATER_PROMPTS + } + }; + } - return { - metadata: { - command: '' - } - }; - } + if (request.command) { + //TODO: Handle command scenarios + } else { const { activeFileParams } = getActiveEditorContent(); const { componentInfo, entityName }: IComponentInfo = await getComponentInfo(this.telemetry, this.orgUrl, activeFileParams, this.powerPagesAgentSessionId); diff --git a/src/common/chat-participants/powerpages/PowerPagesChatParticipantConstants.ts b/src/common/chat-participants/powerpages/PowerPagesChatParticipantConstants.ts index afb3d559..5205a6b6 100644 --- a/src/common/chat-participants/powerpages/PowerPagesChatParticipantConstants.ts +++ b/src/common/chat-participants/powerpages/PowerPagesChatParticipantConstants.ts @@ -6,7 +6,7 @@ import * as vscode from 'vscode'; import { ADX_ENTITYFORM, ADX_ENTITYLIST } from '../../copilot/constants'; -export const NO_PROMPT_MESSAGE = vscode.l10n.t('Please provide a prompt to get started.\n You can get help with writing code for Power Pages sites in HTML, CSS, and JS languages.'); +export const NO_PROMPT_MESSAGE = vscode.l10n.t('Hi! Power Pages lets you build secure, professional websites that you can quickly configure and publish across web browsers and devices.\n\nTo create your website, visit the [Power Pages](https://powerpages.microsoft.com/).\nReturn to this chat and @powerpages can help you write and edit your website code.'); export const POWERPAGES_CHAT_PARTICIPANT_ID = 'powerpages'; export const RESPONSE_AWAITED_MSG = vscode.l10n.t('Working on it...'); export const AUTHENTICATION_FAILED_MSG = vscode.l10n.t('Authentication failed. Please try again.'); @@ -20,5 +20,12 @@ export const VSCODE_EXTENSION_GITHUB_POWER_PAGES_AGENT_ORG_DETAILS = 'GitHubPowe export const VSCODE_EXTENSION_GITHUB_POWER_PAGES_AGENT_ORG_DETAILS_NOT_FOUND = 'GitHubPowerPagesAgentOrgDetailsNotFound'; export const VSCODE_EXTENSION_GITHUB_POWER_PAGES_AGENT_SCENARIO = 'GitHubPowerPagesAgentScenario'; export const SKIP_CODES = ["", null, undefined, "violation", "unclear", "explain"]; +export const EXPLAIN_CODE_PROMPT = vscode.l10n.t('Explain the following code {% include \'Page Copy\'%}'); +export const WEB_API_PROMPT = vscode.l10n.t('Write web API code to query active contact records.'); +export const FORM_PROMPT = vscode.l10n.t('Write JavaScript code for form field validation to check phone field value is in the valid format.'); +export const LIST_PROMPT = vscode.l10n.t('Write JavaScript code to highlight the row where email field is empty in table list.'); +export const STATER_PROMPTS = "starterPrompts" +export const WELCOME_PROMPT = 'how can you help with coding for my website?' +export const WELCOME_MESSAGE = vscode.l10n.t('Hi! @powerpages can help you write, edit, and even summarize your website code.') diff --git a/src/common/chat-participants/powerpages/PowerPagesChatParticipantUtils.ts b/src/common/chat-participants/powerpages/PowerPagesChatParticipantUtils.ts index 96128ead..94ff0628 100644 --- a/src/common/chat-participants/powerpages/PowerPagesChatParticipantUtils.ts +++ b/src/common/chat-participants/powerpages/PowerPagesChatParticipantUtils.ts @@ -10,8 +10,9 @@ import { ITelemetry } from "../../OneDSLoggerTelemetry/telemetry/ITelemetry"; import { ArtemisService } from "../../services/ArtemisService"; import { dataverseAuthentication } from "../../services/AuthenticationProvider"; import { IIntelligenceAPIEndpointInformation } from "../../services/Interfaces"; -import { SUPPORTED_ENTITIES } from "./PowerPagesChatParticipantConstants"; -import { IComponentInfo } from "./PowerPagesChatParticipantTypes"; +import { EXPLAIN_CODE_PROMPT, FORM_PROMPT, LIST_PROMPT, STATER_PROMPTS, SUPPORTED_ENTITIES, WEB_API_PROMPT } from "./PowerPagesChatParticipantConstants"; +import { IComponentInfo, IPowerPagesChatResult } from "./PowerPagesChatParticipantTypes"; +import * as vscode from 'vscode'; export async function getEndpoint( orgID: string, @@ -55,3 +56,15 @@ export async function getComponentInfo(telemetry: ITelemetry, orgUrl: string | u export function isEntityInSupportedList(entity: string): boolean { return SUPPORTED_ENTITIES.includes(entity); } + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +export function provideChatParticipantFollowups(result: IPowerPagesChatResult, _context: vscode.ChatContext, _token: vscode.CancellationToken) { + if (result.metadata.command === STATER_PROMPTS) { + return [ + { prompt: EXPLAIN_CODE_PROMPT }, + { prompt: WEB_API_PROMPT }, + { prompt: LIST_PROMPT }, + { prompt: FORM_PROMPT } + ]; + } +} diff --git a/src/common/copilot/PowerPagesCopilot.ts b/src/common/copilot/PowerPagesCopilot.ts index 2af75ebf..bd6a2ce0 100644 --- a/src/common/copilot/PowerPagesCopilot.ts +++ b/src/common/copilot/PowerPagesCopilot.ts @@ -297,7 +297,7 @@ export class PowerPagesCopilot implements vscode.WebviewViewProvider { case "openGitHubCopilotLink": { //Open the GitHub Copilot Chat with @powerpages if GitHub Copilot Chat is installed if(vscode.extensions.getExtension('github.copilot-chat')) { - vscode.commands.executeCommand('workbench.action.chat.open', '@powerpages'); + vscode.commands.executeCommand('workbench.action.chat.open', '@powerpages how can you help with coding for my website?'); } else { vscode.env.openExternal(vscode.Uri.parse('https://go.microsoft.com/fwlink/?linkid=2276973')); } diff --git a/src/common/copilot/welcome-notification/CopilotNotificationPanel.ts b/src/common/copilot/welcome-notification/CopilotNotificationPanel.ts index e2191865..bef817c2 100644 --- a/src/common/copilot/welcome-notification/CopilotNotificationPanel.ts +++ b/src/common/copilot/welcome-notification/CopilotNotificationPanel.ts @@ -4,9 +4,9 @@ */ import * as vscode from "vscode"; -import { getNonce, openWalkthrough } from "../../utilities/Utils"; +import { getNonce } from "../../utilities/Utils"; import TelemetryReporter from "@vscode/extension-telemetry"; -import { CopilotNotificationDoNotShowChecked, CopilotTryNotificationClickedEvent, CopilotWalkthroughEvent, CopilotNotificationDoNotShowUnchecked } from "../telemetry/telemetryConstants"; +import { CopilotNotificationDoNotShowChecked, CopilotTryNotificationClickedEvent, CopilotNotificationDoNotShowUnchecked } from "../telemetry/telemetryConstants"; import { COPILOT_NOTIFICATION_DISABLED } from "../constants"; import { oneDSLoggerWrapper } from "../../OneDSLoggerTelemetry/oneDSLoggerWrapper"; @@ -14,86 +14,101 @@ let NotificationPanel: vscode.WebviewPanel | undefined; export async function copilotNotificationPanel(context: vscode.ExtensionContext, telemetry: TelemetryReporter, telemetryData: string, countOfActivePortals?: string) { - if (NotificationPanel) { - NotificationPanel.dispose(); - } - - NotificationPanel = createNotificationPanel(); - - const { notificationCssUri, notificationJsUri, copilotImageUri, arrowImageUri } = getWebviewURIs(context, NotificationPanel); - - const nonce = getNonce(); - const webview = NotificationPanel.webview - NotificationPanel.webview.html = getWebviewContent(notificationCssUri, notificationJsUri, copilotImageUri, arrowImageUri, nonce, webview); - - NotificationPanel.webview.onDidReceiveMessage( - async message => { - switch (message.command) { - case 'checked': - telemetry.sendTelemetryEvent(CopilotNotificationDoNotShowChecked, { listOfOrgs: telemetryData, countOfActivePortals: countOfActivePortals as string }); - oneDSLoggerWrapper.getLogger().traceInfo(CopilotNotificationDoNotShowChecked, { listOfOrgs: telemetryData, countOfActivePortals: countOfActivePortals as string }); - context.globalState.update(COPILOT_NOTIFICATION_DISABLED, true); - break; - case 'unchecked': - telemetry.sendTelemetryEvent(CopilotNotificationDoNotShowUnchecked, { listOfOrgs: telemetryData, countOfActivePortals: countOfActivePortals as string }); - oneDSLoggerWrapper.getLogger().traceInfo(CopilotNotificationDoNotShowUnchecked, { listOfOrgs: telemetryData, countOfActivePortals: countOfActivePortals as string }); - context.globalState.update(COPILOT_NOTIFICATION_DISABLED, false); - break; - case 'tryCopilot': - telemetry.sendTelemetryEvent(CopilotTryNotificationClickedEvent, { listOfOrgs: telemetryData, countOfActivePortals: countOfActivePortals as string }); - oneDSLoggerWrapper.getLogger().traceInfo(CopilotTryNotificationClickedEvent, { listOfOrgs: telemetryData, countOfActivePortals: countOfActivePortals as string }); - vscode.commands.executeCommand('powerpages.copilot.focus') - NotificationPanel?.dispose(); - break; - case 'learnMore': - telemetry.sendTelemetryEvent(CopilotWalkthroughEvent, { listOfOrgs: telemetryData, countOfActivePortals: countOfActivePortals as string }); - openWalkthrough(context.extensionUri); - } - }, - undefined, - context.subscriptions - ); -} + if (NotificationPanel) { + NotificationPanel.dispose(); + } -function createNotificationPanel(): vscode.WebviewPanel { - const NotificationPanel = vscode.window.createWebviewPanel( - "CopilotNotification", - "Copilot in Power Pages", - { - viewColumn: vscode.ViewColumn.Beside, - preserveFocus: true, - }, - { - enableScripts: true, + NotificationPanel = createNotificationPanel(); + + const { notificationCssUri, notificationJsUri, copilotImageUri, arrowImageUri } = getWebviewURIs(context, NotificationPanel); + + const nonce = getNonce(); + const webview = NotificationPanel.webview + let isGitHubCopilotPresent = false; + let GITHUB_COPILOT_CHAT: string; + + if (vscode.extensions.getExtension('github.copilot-chat')) { + GITHUB_COPILOT_CHAT = vscode.l10n.t('Try @powerpages with GitHub Copilot'); + isGitHubCopilotPresent = true; + } else { + GITHUB_COPILOT_CHAT = vscode.l10n.t('Get GitHub Copilot to try @powerpages'); } - ); - return NotificationPanel; + NotificationPanel.webview.html = getWebviewContent(notificationCssUri, notificationJsUri, copilotImageUri, arrowImageUri, nonce, webview, GITHUB_COPILOT_CHAT); + + NotificationPanel.webview.onDidReceiveMessage( + async message => { + switch (message.command) { + case 'checked': + telemetry.sendTelemetryEvent(CopilotNotificationDoNotShowChecked, { listOfOrgs: telemetryData, countOfActivePortals: countOfActivePortals as string }); + oneDSLoggerWrapper.getLogger().traceInfo(CopilotNotificationDoNotShowChecked, { listOfOrgs: telemetryData, countOfActivePortals: countOfActivePortals as string }); + context.globalState.update(COPILOT_NOTIFICATION_DISABLED, true); + break; + case 'unchecked': + telemetry.sendTelemetryEvent(CopilotNotificationDoNotShowUnchecked, { listOfOrgs: telemetryData, countOfActivePortals: countOfActivePortals as string }); + oneDSLoggerWrapper.getLogger().traceInfo(CopilotNotificationDoNotShowUnchecked, { listOfOrgs: telemetryData, countOfActivePortals: countOfActivePortals as string }); + context.globalState.update(COPILOT_NOTIFICATION_DISABLED, false); + break; + case 'tryCopilot': + telemetry.sendTelemetryEvent(CopilotTryNotificationClickedEvent, { listOfOrgs: telemetryData, countOfActivePortals: countOfActivePortals as string }); + oneDSLoggerWrapper.getLogger().traceInfo(CopilotTryNotificationClickedEvent, { listOfOrgs: telemetryData, countOfActivePortals: countOfActivePortals as string }); + vscode.commands.executeCommand('powerpages.copilot.focus') + NotificationPanel?.dispose(); + break; + case 'learnMore': + // telemetry.sendTelemetryEvent(CopilotWalkthroughEvent, { listOfOrgs: telemetryData, countOfActivePortals: countOfActivePortals as string }); + // openWalkthrough(context.extensionUri); + if (isGitHubCopilotPresent) { + vscode.commands.executeCommand('workbench.action.chat.open', '@powerpages how can you help with coding for my website?'); + } else { + vscode.env.openExternal(vscode.Uri.parse('https://go.microsoft.com/fwlink/?linkid=2276973')); + } + } + }, + undefined, + context.subscriptions + ); +} + +function createNotificationPanel(): vscode.WebviewPanel { + const NotificationPanel = vscode.window.createWebviewPanel( + "CopilotNotification", + "Copilot in Power Pages", + { + viewColumn: vscode.ViewColumn.Beside, + preserveFocus: true, + }, + { + enableScripts: true, + } + ); + + return NotificationPanel; } function getWebviewURIs(context: vscode.ExtensionContext, NotificationPanel: vscode.WebviewPanel): { notificationCssUri: vscode.Uri, notificationJsUri: vscode.Uri, copilotImageUri: vscode.Uri, arrowImageUri: vscode.Uri } { - const srcPath = vscode.Uri.joinPath(context.extensionUri, 'src', 'common', 'copilot', "welcome-notification"); + const srcPath = vscode.Uri.joinPath(context.extensionUri, 'src', 'common', 'copilot', "welcome-notification"); - const notificationCssPath = vscode.Uri.joinPath(srcPath, "copilotNotification.css"); - const notificationCssUri = NotificationPanel.webview.asWebviewUri(notificationCssPath); + const notificationCssPath = vscode.Uri.joinPath(srcPath, "copilotNotification.css"); + const notificationCssUri = NotificationPanel.webview.asWebviewUri(notificationCssPath); - const notificationJsPath = vscode.Uri.joinPath(srcPath, "copilotNotification.js"); - const notificationJsUri = NotificationPanel.webview.asWebviewUri(notificationJsPath); + const notificationJsPath = vscode.Uri.joinPath(srcPath, "copilotNotification.js"); + const notificationJsUri = NotificationPanel.webview.asWebviewUri(notificationJsPath); - const copilotImagePath = vscode.Uri.joinPath(srcPath, "notification.svg"); - const copilotImageUri = NotificationPanel.webview.asWebviewUri(copilotImagePath); + const copilotImagePath = vscode.Uri.joinPath(srcPath, "notification.svg"); + const copilotImageUri = NotificationPanel.webview.asWebviewUri(copilotImagePath); - const arrowImagePath = vscode.Uri.joinPath(srcPath, "arrow.svg"); - const arrowImageUri = NotificationPanel.webview.asWebviewUri(arrowImagePath); + const arrowImagePath = vscode.Uri.joinPath(srcPath, "arrow.svg"); + const arrowImageUri = NotificationPanel.webview.asWebviewUri(arrowImagePath); - return { notificationCssUri, notificationJsUri, copilotImageUri, arrowImageUri }; + return { notificationCssUri, notificationJsUri, copilotImageUri, arrowImageUri }; } -function getWebviewContent(notificationCssUri: vscode.Uri, notificationJsUri: vscode.Uri, copilotImageUri: vscode.Uri, arrowImageUri: vscode.Uri, nonce: string, webview: vscode.Webview) { +function getWebviewContent(notificationCssUri: vscode.Uri, notificationJsUri: vscode.Uri, copilotImageUri: vscode.Uri, arrowImageUri: vscode.Uri, nonce: string, webview: vscode.Webview, GITHUB_COPILOT_CHAT: string) { - return ` + return `
@@ -108,8 +123,8 @@ function getWebviewContent(notificationCssUri: vscode.Uri, notificationJsUri: vs
${vscode.l10n.t("Whether it’s HTML, CSS, JS, or Liquid code, just describe what you need and let AI build it for you. ")}
- - ${vscode.l10n.t("Learn more about Copilot")} + + ${vscode.l10n.t(GITHUB_COPILOT_CHAT)}