diff --git a/l10n/bundle.l10n.json b/l10n/bundle.l10n.json index a2e82af1..f41bfd9e 100644 --- a/l10n/bundle.l10n.json +++ b/l10n/bundle.l10n.json @@ -57,12 +57,16 @@ "In your own words, describe what you need. You can get help with writing code for Power Pages sites in HTML, CSS, and JS languages.": "In your own words, describe what you need. You can get help with writing code for Power Pages sites in HTML, CSS, and JS languages.", "To know more, see Copilot in Power Pages documentation.": "To know more, see Copilot in Power Pages documentation.", "Working on it...": "Working on it...", + "You can use this in GitHub Copilot with @powerpages and leverage best of both world.": "You can use this in GitHub Copilot with @powerpages and leverage best of both world.", + "NEW": "NEW", "Copied to clipboard!": "Copied to clipboard!", "What do you need help with?": "What do you need help with?", "Make sure AI-generated content is accurate and appropriate before using. Learn more | View\n terms": "Make sure AI-generated content is accurate and appropriate before using. Learn more | View\n terms", + "Try @powerpages with GitHub Copilot": "Try @powerpages with GitHub Copilot", + "Get GitHub Copilot to try @powerpages": "Get GitHub Copilot to try @powerpages", "Let Copilot help you code": "Let Copilot help you code", "Whether it’s HTML, CSS, JS, or Liquid code, just describe what you need and let AI build it for you. ": "Whether it’s HTML, CSS, JS, or Liquid code, just describe what you need and let AI build it for you. ", - "Try Copilot for Power Pages": "Try Copilot for Power Pages", + "Continue with Copilot for Power Pages": "Continue with Copilot for Power Pages", "Do not show again": "Do not show again", "Like something? Tell us more.": "Like something? Tell us more.", "Dislike something? Tell us more.": "Dislike something? Tell us more.", @@ -70,10 +74,13 @@ "Tell us more.": "Tell us more.", "Try and be as specific as possible. Your feedback will be used to improve Copilot. View privacy details ": "Try and be as specific as possible. Your feedback will be used to improve Copilot. View privacy details ", "Submit": "Submit", - "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.": "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.", + "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.": "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.", "Authentication failed. Please try again.": "Authentication failed. Please try again.", "Copilot is not available. Please contact your administrator.": "Copilot is not available. Please contact your administrator.", "Active auth profile is not found or has expired. Please try again.": "Active auth profile is not found or has expired. Please try again.", + "Make sure AI-generated content is accurate and appropriate before using. [Learn more](https://go.microsoft.com/fwlink/?linkid=2240145) | [View terms](https://go.microsoft.com/fwlink/?linkid=2189520)": "Make sure AI-generated content is accurate and appropriate before using. [Learn more](https://go.microsoft.com/fwlink/?linkid=2240145) | [View terms](https://go.microsoft.com/fwlink/?linkid=2189520)", + "Explain the following code {% include 'Page Copy'%}": "Explain the following code {% include 'Page Copy'%}", + "Hi! @powerpages can help you write, edit, and even summarize your website code.": "Hi! @powerpages can help you write, edit, and even summarize your website code.", "Select Folder for new PCF Control/Do not translate 'PCF' as it is a product name.": { "message": "Select Folder for new PCF Control", "comment": [ diff --git a/loc/translations-export/vscode-powerplatform.xlf b/loc/translations-export/vscode-powerplatform.xlf index 7c87315d..69c3dcd4 100644 --- a/loc/translations-export/vscode-powerplatform.xlf +++ b/loc/translations-export/vscode-powerplatform.xlf @@ -79,6 +79,9 @@ Confirm + + Continue with Copilot for Power Pages + Copied to clipboard! @@ -134,6 +137,9 @@ The {3} represents Solution's Type (Managed or Unmanaged), but that test is loca Explain the following code snippet: + + Explain the following code {% include 'Page Copy'%} + Failed to create: {0}. {0} will be replaced by the error message. @@ -169,15 +175,27 @@ The {3} represents Solution's Type (Managed or Unmanaged), but that test is loca GETTING STARTED + + Get GitHub Copilot to try @powerpages + Here are a few suggestions to get you started Hi + + Hi! @powerpages can help you write, edit, and even summarize your website code. + Hi! Instantly generate code for Power Pages sites by typing in what you need. To start using Copilot, log in to your Microsoft account. + + Hi! Power Pages lets you build secure, professional websites that you can quickly configure and publish across web browsers and devices. + +To create your website, visit the [Power Pages](https://powerpages.microsoft.com/). +Return to this chat and @powerpages can help you write and edit your website code. + Hi! Your Microsoft account doesn’t currently support Copilot. Contact your admin for details. @@ -207,6 +225,9 @@ The {3} represents Solution's Type (Managed or Unmanaged), but that test is loca Make sure AI-generated content is accurate and appropriate before using. <a href="https://go.microsoft.com/fwlink/?linkid=2240145">Learn more</a> | <a href="https://go.microsoft.com/fwlink/?linkid=2189520">View terms</a> + + Make sure AI-generated content is accurate and appropriate before using. [Learn more](https://go.microsoft.com/fwlink/?linkid=2240145) | [View terms](https://go.microsoft.com/fwlink/?linkid=2189520) + Managed @@ -216,6 +237,9 @@ The {3} represents Solution's Type (Managed or Unmanaged), but that test is loca Microsoft wants your feeback + + NEW + Name/Rename Auth Profile @@ -294,10 +318,6 @@ The {3} represents Dataverse Environment's Organization ID (GUID) Please enter a name for the webpage. - - Please provide a prompt to get started. - You can get help with writing code for Power Pages sites in HTML, CSS, and JS languages. - Power Pages Copilot is now connected to the environment: {0} : {1} {0} represents the environment name @@ -380,8 +400,8 @@ The {3} represents Dataverse Environment's Organization ID (GUID) To know more, see <a href="https://go.microsoft.com/fwlink/?linkid=2206366">Copilot in Power Pages documentation. - - Try Copilot for Power Pages + + Try @powerpages with GitHub Copilot Try again @@ -435,6 +455,9 @@ The {3} represents Dataverse Environment's Organization ID (GUID) You are editing a live, public site + + You can use this in <a href="#" id="github-copilot-link">GitHub Copilot with @powerpages</a> and leverage best of both world. + dotnet sdk 6.0 or greater must be installed Do not translate 'dotnet' or 'sdk' diff --git a/src/common/chat-participants/powerpages/PowerPagesChatParticipant.ts b/src/common/chat-participants/powerpages/PowerPagesChatParticipant.ts index 880b3525..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, 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); @@ -162,7 +172,7 @@ export class PowerPagesChatParticipant { } stream.markdown('\n'); }); - + stream.markdown(DISCLAIMER_MESSAGE); } return { diff --git a/src/common/chat-participants/powerpages/PowerPagesChatParticipantConstants.ts b/src/common/chat-participants/powerpages/PowerPagesChatParticipantConstants.ts index 0a0f8271..5205a6b6 100644 --- a/src/common/chat-participants/powerpages/PowerPagesChatParticipantConstants.ts +++ b/src/common/chat-participants/powerpages/PowerPagesChatParticipantConstants.ts @@ -6,12 +6,13 @@ 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.'); export const COPILOT_NOT_AVAILABLE_MSG = vscode.l10n.t('Copilot is not available. Please contact your administrator.'); export const PAC_AUTH_NOT_FOUND = vscode.l10n.t('Active auth profile is not found or has expired. Please try again.'); +export const DISCLAIMER_MESSAGE = vscode.l10n.t('Make sure AI-generated content is accurate and appropriate before using. [Learn more](https://go.microsoft.com/fwlink/?linkid=2240145) | [View terms](https://go.microsoft.com/fwlink/?linkid=2189520)'); export const SUPPORTED_ENTITIES = [ADX_ENTITYFORM, ADX_ENTITYLIST]; // Telemetry Event Names export const VSCODE_EXTENSION_GITHUB_POWER_PAGES_AGENT_INVOKED = 'GitHubPowerPagesAgentInvoked'; @@ -19,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 ba7f53e3..e225848c 100644 --- a/src/common/copilot/PowerPagesCopilot.ts +++ b/src/common/copilot/PowerPagesCopilot.ts @@ -10,7 +10,7 @@ import { dataverseAuthentication, getOIDFromToken, intelligenceAPIAuthentication import { v4 as uuidv4 } from 'uuid' import { PacWrapper } from "../../client/pac/PacWrapper"; import { ITelemetry } from "../OneDSLoggerTelemetry/telemetry/ITelemetry"; -import { ADX_ENTITYFORM, ADX_ENTITYLIST, AUTH_CREATE_FAILED, AUTH_CREATE_MESSAGE, AuthProfileNotFound, COPILOT_UNAVAILABLE, CopilotStylePathSegments, EXPLAIN_CODE, SELECTED_CODE_INFO, SELECTED_CODE_INFO_ENABLED, THUMBS_DOWN, THUMBS_UP, UserPrompt, WebViewMessage, sendIconSvg } from "./constants"; +import { ADX_ENTITYFORM, ADX_ENTITYLIST, AUTH_CREATE_FAILED, AUTH_CREATE_MESSAGE, AuthProfileNotFound, COPILOT_IN_POWERPAGES, COPILOT_UNAVAILABLE, CopilotStylePathSegments, EXPLAIN_CODE, SELECTED_CODE_INFO, SELECTED_CODE_INFO_ENABLED, THUMBS_DOWN, THUMBS_UP, UserPrompt, WebViewMessage, sendIconSvg } from "./constants"; import { IActiveFileParams, IOrgInfo } from './model'; import { checkCopilotAvailability, escapeDollarSign, getActiveEditorContent, getNonce, getSelectedCode, getSelectedCodeLineRange, getUserName, openWalkthrough, showConnectedOrgMessage, showInputBoxAndGetOrgUrl, showProgressWithNotification } from "../utilities/Utils"; import { CESUserFeedback } from "./user-feedback/CESSurvey"; @@ -148,7 +148,7 @@ export class PowerPagesCopilot implements vscode.WebviewViewProvider { ) { this._view = webviewView; - webviewView.title = vscode.l10n.t("Copilot In Power Pages") + (IS_DESKTOP ? "" : " [PREVIEW]"); + webviewView.title = vscode.l10n.t(COPILOT_IN_POWERPAGES) + (IS_DESKTOP ? "" : " [PREVIEW]"); webviewView.description = vscode.l10n.t("PREVIEW"); webviewView.webview.options = { // Allow scripts in the webview @@ -194,9 +194,11 @@ export class PowerPagesCopilot implements vscode.WebviewViewProvider { LOGIN_MESSAGE: vscode.l10n.t("Hi! Instantly generate code for Power Pages sites by typing in what you need. To start using Copilot, log in to your Microsoft account."), LOGIN_BUTTON: vscode.l10n.t("Login"), HI: vscode.l10n.t("Hi"), - WELCOME_MESSAGE: vscode.l10n.t(`In your own words, describe what you need. You can get help with writing code for Power Pages sites in HTML, CSS, and JS languages.`), + WELCOME_MESSAGE: vscode.l10n.t('In your own words, describe what you need. You can get help with writing code for Power Pages sites in HTML, CSS, and JS languages.'), DOCUMENTATION_LINK: vscode.l10n.t("To know more, see Copilot in Power Pages documentation."), - WORKING_ON_IT_MESSAGE: vscode.l10n.t("Working on it...") + WORKING_ON_IT_MESSAGE: vscode.l10n.t("Working on it..."), + GITHUB_COPILOT_CHAT: vscode.l10n.t('You can use this in GitHub Copilot with @powerpages and leverage best of both world.'), + NEW_BADGE: vscode.l10n.t("NEW"), }; this.sendMessageToWebview({ @@ -214,7 +216,7 @@ export class PowerPagesCopilot implements vscode.WebviewViewProvider { } sendTelemetryEvent(this.telemetry, { eventName: CopilotLoadedEvent, copilotSessionId: sessionID, orgId: orgID }); - this.sendMessageToWebview({ type: 'env' }); //TODO Use IS_DESKTOP + this.sendMessageToWebview({ type: 'env' }); await this.checkAuthentication(); if (orgID && userName) { this.sendMessageToWebview({ type: 'isLoggedIn', value: true }); @@ -292,6 +294,14 @@ export class PowerPagesCopilot implements vscode.WebviewViewProvider { sendTelemetryEvent(this.telemetry, { eventName: CopilotCodeLineCountEvent, copilotSessionId: sessionID, codeLineCount: String(data.value), orgId: orgID }); break; } + 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 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/assets/scripts/copilot.js b/src/common/copilot/assets/scripts/copilot.js index 34b92d9d..813e39c7 100644 --- a/src/common/copilot/assets/scripts/copilot.js +++ b/src/common/copilot/assets/scripts/copilot.js @@ -27,7 +27,6 @@ let copilotStrings = {}; - const inputHistory = []; let currentIndex = -1; @@ -241,6 +240,16 @@ return feedback; } + function createGitHubCopilotLinkDiv() { + const gitHubCopilotText = document.createElement("div"); + gitHubCopilotText.classList.add("github-copilot-text"); + + gitHubCopilotText.innerHTML = `${copilotStrings.NEW_BADGE} + ${copilotStrings.GITHUB_COPILOT_CHAT}` + + return gitHubCopilotText; + } + function createSuggestedPromptDiv() { const suggestedPrompt = document.createElement("div"); suggestedPrompt.classList.add("suggested-prompts"); @@ -384,6 +393,17 @@ `; messageElement.appendChild(loggedInDiv); + // Add GitHub Copilot link + + const gitHubCopilotLink = createGitHubCopilotLinkDiv(); + messageElement.appendChild(gitHubCopilotLink); + + const gitHubCopilotLinkElement = document.getElementById("github-copilot-link"); + gitHubCopilotLinkElement.addEventListener("click", () => { + vscode.postMessage({ type: "openGitHubCopilotLink" }); + }); + + const suggestedPromptDiv = createSuggestedPromptDiv(); messageElement.appendChild(suggestedPromptDiv); @@ -441,7 +461,6 @@ break; } case "env": { - welcomeScreen = setWelcomeScreen(); break; } diff --git a/src/common/copilot/assets/styles/copilot.css b/src/common/copilot/assets/styles/copilot.css index b29e9812..24c66208 100644 --- a/src/common/copilot/assets/styles/copilot.css +++ b/src/common/copilot/assets/styles/copilot.css @@ -250,6 +250,10 @@ a:focus { outline: none; } +a:hover { + text-decoration: underline; +} + li:before { content: "/"; } @@ -339,3 +343,12 @@ hr { margin-right: 4px; /* Add some spacing between icon and text */ } + +.new-badge { + background-color: var(--vscode-button-background); + color: var(--vscode-button-foreground); + border-radius: 50px; + padding: 1px 5px; + font-size: 9px; + /* margin-right: 2px; */ +} diff --git a/src/common/copilot/constants.ts b/src/common/copilot/constants.ts index 1c313a09..4e32e884 100644 --- a/src/common/copilot/constants.ts +++ b/src/common/copilot/constants.ts @@ -35,7 +35,7 @@ export const ATTRIBUTE_DESCRIPTION = 'description'; export const ATTRIBUTE_DATAFIELD_NAME = 'datafieldname'; export const ATTRIBUTE_CLASSID = 'classid'; export const SYSTEFORMS_API_PATH = 'api/data/v9.2/systemforms'; - +export const COPILOT_IN_POWERPAGES = 'Copilot In Power Pages' export type WebViewMessage = { type: string; diff --git a/src/common/copilot/welcome-notification/CopilotNotificationPanel.ts b/src/common/copilot/welcome-notification/CopilotNotificationPanel.ts index e2191865..28fcb98f 100644 --- a/src/common/copilot/welcome-notification/CopilotNotificationPanel.ts +++ b/src/common/copilot/welcome-notification/CopilotNotificationPanel.ts @@ -4,96 +4,111 @@ */ 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 { COPILOT_NOTIFICATION_DISABLED } from "../constants"; +import { CopilotNotificationDoNotShowChecked, CopilotTryNotificationClickedEvent, CopilotNotificationDoNotShowUnchecked } from "../telemetry/telemetryConstants"; +import { COPILOT_IN_POWERPAGES, COPILOT_NOTIFICATION_DISABLED } from "../constants"; import { oneDSLoggerWrapper } from "../../OneDSLoggerTelemetry/oneDSLoggerWrapper"; 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_POWERPAGES, + { + 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, githubCopilotChat: string) { - return ` + return ` @@ -108,8 +123,8 @@ function getWebviewContent(notificationCssUri: vscode.Uri, notificationJsUri: vs

${vscode.l10n.t("Let Copilot help you code")}

${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(githubCopilotChat)}
Image @@ -126,9 +141,9 @@ function getWebviewContent(notificationCssUri: vscode.Uri, notificationJsUri: vs } export function disposeNotificationPanel() { - if (NotificationPanel) { - NotificationPanel.dispose(); - NotificationPanel = undefined; - } + if (NotificationPanel) { + NotificationPanel.dispose(); + NotificationPanel = undefined; + } }