From b5eff917b51fb30d97d965cee604dc34f9516141 Mon Sep 17 00:00:00 2001 From: amitjoshi Date: Wed, 20 Nov 2024 12:13:06 +0530 Subject: [PATCH 01/13] Enhance CreateSiteCommand to include extension context and add ReadonlyFileSystemProvider for site page previews --- .../commands/create-site/CreateSiteCommand.ts | 5 ++- .../commands/create-site/CreateSiteHelper.ts | 45 ++++++++++++++++++- .../utilities/ReadonlyFileSystemProvider.ts | 27 +++++++++++ 3 files changed, 73 insertions(+), 4 deletions(-) create mode 100644 src/common/utilities/ReadonlyFileSystemProvider.ts diff --git a/src/common/chat-participants/powerpages/commands/create-site/CreateSiteCommand.ts b/src/common/chat-participants/powerpages/commands/create-site/CreateSiteCommand.ts index b2db45a2..5d3f5de6 100644 --- a/src/common/chat-participants/powerpages/commands/create-site/CreateSiteCommand.ts +++ b/src/common/chat-participants/powerpages/commands/create-site/CreateSiteCommand.ts @@ -13,7 +13,7 @@ import { VSCODE_EXTENSION_CREATE_SITE_COMMAND_FAILED} from "../../PowerPagesChat export class CreateSiteCommand implements Command { // eslint-disable-next-line @typescript-eslint/no-explicit-any async execute(request: any, stream: vscode.ChatResponseStream): Promise { - const { prompt, intelligenceAPIEndpointInfo, intelligenceApiToken, powerPagesAgentSessionId, telemetry, orgId, envId, userId } = request; + const { prompt, intelligenceAPIEndpointInfo, intelligenceApiToken, powerPagesAgentSessionId, telemetry, orgId, envId, userId, extensionContext } = request; stream.progress(NL2SITE_GENERATING_SITE); try { @@ -27,7 +27,8 @@ export class CreateSiteCommand implements Command { telemetry, orgId, envId, - userId + userId, + extensionContext ); // Process the result diff --git a/src/common/chat-participants/powerpages/commands/create-site/CreateSiteHelper.ts b/src/common/chat-participants/powerpages/commands/create-site/CreateSiteHelper.ts index ddf2092a..0ff083c1 100644 --- a/src/common/chat-participants/powerpages/commands/create-site/CreateSiteHelper.ts +++ b/src/common/chat-participants/powerpages/commands/create-site/CreateSiteHelper.ts @@ -10,9 +10,17 @@ import { getNL2SiteData } from './Nl2SiteService'; import { NL2SITE_REQUEST_FAILED, NL2PAGE_GENERATING_WEBPAGES, NL2PAGE_RESPONSE_FAILED } from '../../PowerPagesChatParticipantConstants'; import { oneDSLoggerWrapper } from '../../../../OneDSLoggerTelemetry/oneDSLoggerWrapper'; import { VSCODE_EXTENSION_NL2PAGE_REQUEST, VSCODE_EXTENSION_NL2SITE_REQUEST } from '../../PowerPagesChatParticipantTelemetryConstants'; +import { ReadonlyFileSystemProvider } from '../../../../utilities/ReadonlyFileSystemProvider'; -export const createSite = async (intelligenceEndpoint: string, intelligenceApiToken: string, userPrompt: string, sessionId: string, stream: vscode.ChatResponseStream, telemetry: ITelemetry, orgId: string, envID: string, userId: string) => { - const { siteName, siteDescription } = await fetchSiteAndPageData(intelligenceEndpoint, intelligenceApiToken, userPrompt, sessionId, telemetry, stream, orgId, envID, userId); +export const createSite = async (intelligenceEndpoint: string, intelligenceApiToken: string, userPrompt: string, sessionId: string, stream: vscode.ChatResponseStream, telemetry: ITelemetry, orgId: string, envID: string, userId: string, extensionContext: vscode.ExtensionContext) => { + const { siteName, siteDescription, sitePages} = await fetchSiteAndPageData(intelligenceEndpoint, intelligenceApiToken, userPrompt, sessionId, telemetry, stream, orgId, envID, userId); + + previewSitePagesContent(siteName, sitePages, stream, extensionContext); + + stream.button({ + title: 'Create Site', + command: 'open-site', + }); return { siteName, @@ -46,3 +54,36 @@ async function fetchSiteAndPageData(intelligenceEndpoint: string, intelligenceAp return { siteName, sitePagesList, sitePages, siteDescription }; } + +function previewSitePagesContent( + siteName: string, + sitePages: any[], + stream: vscode.ChatResponseStream, + extensionContext: vscode.ExtensionContext +) { + const sitePagesContent: { name: string; content: string }[] = []; + sitePages.forEach((page: any) => { + sitePagesContent.push({ name: page.metadata.pageTitle, content: page.code }); + }); + + stream.markdown('\nHere is the name of the site: ' + siteName); + + const sitePagesFolder: vscode.ChatResponseFileTree[] = []; + const contentProvider = new ReadonlyFileSystemProvider(); + const scheme = 'readonly'; + // Register the content provider + extensionContext.subscriptions.push( + vscode.workspace.registerTextDocumentContentProvider(scheme, contentProvider) + ); + + const baseUri = vscode.Uri.parse('readonly:/'); + + sitePagesContent.forEach((page: { name: string; content: string; }) => { + sitePagesFolder.push({ name: page.name + '.html' }); + const pageUri = vscode.Uri.joinPath(baseUri, page.name + '.html'); + contentProvider.updateFileContent(pageUri.path, page.content); + }); + + // TODO: pass uri of current workspace as second parameter + stream.filetree(sitePagesFolder, baseUri); +} diff --git a/src/common/utilities/ReadonlyFileSystemProvider.ts b/src/common/utilities/ReadonlyFileSystemProvider.ts new file mode 100644 index 00000000..2da1d7a4 --- /dev/null +++ b/src/common/utilities/ReadonlyFileSystemProvider.ts @@ -0,0 +1,27 @@ +/* + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + */ + +import * as vscode from 'vscode'; + +export class ReadonlyFileSystemProvider implements vscode.TextDocumentContentProvider { + // File content mapping + private fileContentMap: { [key: string]: string } = {}; + + // Provide content for a given URI + provideTextDocumentContent(uri: vscode.Uri): string { + const filePath = uri.path; + return this.fileContentMap[filePath] || 'File not found'; + } + + // Update content for a given file + updateFileContent(filePath: string, content: string) { + this.fileContentMap[filePath] = content; + const uri = vscode.Uri.parse(`readonly:${filePath}`); + this._onDidChangeEmitter.fire(uri); + } + + private _onDidChangeEmitter = new vscode.EventEmitter(); + readonly onDidChange = this._onDidChangeEmitter.event; +} From 17aaad6cfa5eda1120b0244cbe0dec909027bf1c Mon Sep 17 00:00:00 2001 From: amitjoshi Date: Fri, 22 Nov 2024 15:32:47 +0530 Subject: [PATCH 02/13] Implement EditableFileSystemProvider for site page editing and update CreateSiteHelper to utilize it --- .../commands/create-site/CreateSiteHelper.ts | 44 +++++++++--- .../utilities/EditableFileSystemProvider.ts | 69 +++++++++++++++++++ 2 files changed, 104 insertions(+), 9 deletions(-) create mode 100644 src/common/utilities/EditableFileSystemProvider.ts diff --git a/src/common/chat-participants/powerpages/commands/create-site/CreateSiteHelper.ts b/src/common/chat-participants/powerpages/commands/create-site/CreateSiteHelper.ts index 0ff083c1..b96676aa 100644 --- a/src/common/chat-participants/powerpages/commands/create-site/CreateSiteHelper.ts +++ b/src/common/chat-participants/powerpages/commands/create-site/CreateSiteHelper.ts @@ -10,18 +10,34 @@ import { getNL2SiteData } from './Nl2SiteService'; import { NL2SITE_REQUEST_FAILED, NL2PAGE_GENERATING_WEBPAGES, NL2PAGE_RESPONSE_FAILED } from '../../PowerPagesChatParticipantConstants'; import { oneDSLoggerWrapper } from '../../../../OneDSLoggerTelemetry/oneDSLoggerWrapper'; import { VSCODE_EXTENSION_NL2PAGE_REQUEST, VSCODE_EXTENSION_NL2SITE_REQUEST } from '../../PowerPagesChatParticipantTelemetryConstants'; -import { ReadonlyFileSystemProvider } from '../../../../utilities/ReadonlyFileSystemProvider'; +//import { ReadonlyFileSystemProvider } from '../../../../utilities/ReadonlyFileSystemProvider'; +import { EditableFileSystemProvider } from '../../../../utilities/EditableFileSystemProvider'; export const createSite = async (intelligenceEndpoint: string, intelligenceApiToken: string, userPrompt: string, sessionId: string, stream: vscode.ChatResponseStream, telemetry: ITelemetry, orgId: string, envID: string, userId: string, extensionContext: vscode.ExtensionContext) => { const { siteName, siteDescription, sitePages} = await fetchSiteAndPageData(intelligenceEndpoint, intelligenceApiToken, userPrompt, sessionId, telemetry, stream, orgId, envID, userId); - previewSitePagesContent(siteName, sitePages, stream, extensionContext); + const contentProvider = previewSitePagesContent(siteName, sitePages, stream, extensionContext); stream.button({ title: 'Create Site', - command: 'open-site', + command: 'create-site-command', + arguments: [contentProvider, sitePages.map(page => ({ name: page.metadata.pageTitle, content: page.code }))] }); + extensionContext.subscriptions.push( + vscode.commands.registerCommand('create-site-command', async (contentProvider: EditableFileSystemProvider, sitePagesContent: { name: string; content: string }[]) => { + const updatedPages = sitePagesContent.map(page => ({ + name: page.name, + content: getUpdatedPageContent(contentProvider, page.name) + })); + + // Process the updated pages as needed + console.log('Updated Pages:', updatedPages); + + // You can add further logic here to handle the updated pages, such as sending them to a server or saving them. + }) + ); + return { siteName, //websiteId, @@ -55,12 +71,13 @@ async function fetchSiteAndPageData(intelligenceEndpoint: string, intelligenceAp return { siteName, sitePagesList, sitePages, siteDescription }; } + function previewSitePagesContent( siteName: string, sitePages: any[], stream: vscode.ChatResponseStream, extensionContext: vscode.ExtensionContext -) { +): EditableFileSystemProvider { const sitePagesContent: { name: string; content: string }[] = []; sitePages.forEach((page: any) => { sitePagesContent.push({ name: page.metadata.pageTitle, content: page.code }); @@ -69,21 +86,30 @@ function previewSitePagesContent( stream.markdown('\nHere is the name of the site: ' + siteName); const sitePagesFolder: vscode.ChatResponseFileTree[] = []; - const contentProvider = new ReadonlyFileSystemProvider(); - const scheme = 'readonly'; + const contentProvider = new EditableFileSystemProvider(); + const scheme = 'editable'; // Register the content provider extensionContext.subscriptions.push( - vscode.workspace.registerTextDocumentContentProvider(scheme, contentProvider) + vscode.workspace.registerFileSystemProvider(scheme, contentProvider, { isCaseSensitive: true }) ); - const baseUri = vscode.Uri.parse('readonly:/'); + const baseUri = vscode.Uri.parse('editable:/'); sitePagesContent.forEach((page: { name: string; content: string; }) => { sitePagesFolder.push({ name: page.name + '.html' }); const pageUri = vscode.Uri.joinPath(baseUri, page.name + '.html'); - contentProvider.updateFileContent(pageUri.path, page.content); + contentProvider.writeFile(pageUri, Buffer.from(page.content, 'utf8')); }); // TODO: pass uri of current workspace as second parameter stream.filetree(sitePagesFolder, baseUri); + + return contentProvider; } + +// Function to get updated content +export function getUpdatedPageContent(contentProvider: EditableFileSystemProvider, pageName: string): string { + const pageUri = vscode.Uri.parse(`editable:/${pageName}.html`); + return contentProvider.getFileContent(pageUri); +} + diff --git a/src/common/utilities/EditableFileSystemProvider.ts b/src/common/utilities/EditableFileSystemProvider.ts new file mode 100644 index 00000000..9320a990 --- /dev/null +++ b/src/common/utilities/EditableFileSystemProvider.ts @@ -0,0 +1,69 @@ +/* + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + */ + +/* eslint-disable @typescript-eslint/no-unused-vars */ + +import * as vscode from 'vscode'; + + +export class EditableFileSystemProvider implements vscode.FileSystemProvider { + private fileContentMap: { [key: string]: Uint8Array } = {}; + private _onDidChangeEmitter = new vscode.EventEmitter(); + readonly onDidChangeFile = this._onDidChangeEmitter.event; + + watch(uri: vscode.Uri, options: { readonly recursive: boolean; readonly excludes: readonly string[]; }): vscode.Disposable { + // For simplicity, this implementation does not support file watching. + // eslint-disable-next-line @typescript-eslint/no-empty-function + return new vscode.Disposable(() => {}); + } + + copy(source: vscode.Uri, destination: vscode.Uri, options: { readonly overwrite: boolean; }): void | Thenable { + if (!options.overwrite && this.fileContentMap[destination.path]) { + throw vscode.FileSystemError.FileExists(destination); + } + this.fileContentMap[destination.path] = this.fileContentMap[source.path]; + this._onDidChangeEmitter.fire([{ type: vscode.FileChangeType.Created, uri: destination }]); + } + + // Read file content + readFile(uri: vscode.Uri): Uint8Array { + const filePath = uri.path; + return this.fileContentMap[filePath] || new Uint8Array(); + } + + // Write file content + writeFile(uri: vscode.Uri, content: Uint8Array): void { + const filePath = uri.path; + this.fileContentMap[filePath] = content; + this._onDidChangeEmitter.fire([{ type: vscode.FileChangeType.Changed, uri }]); + } + + // Other required methods for FileSystemProvider + stat(uri: vscode.Uri): vscode.FileStat { + return { type: vscode.FileType.File, ctime: Date.now(), mtime: Date.now(), size: this.fileContentMap[uri.path]?.length || 0 }; + } + + readDirectory(uri: vscode.Uri): [string, vscode.FileType][] { + return []; + } + + // eslint-disable-next-line @typescript-eslint/no-empty-function + createDirectory(uri: vscode.Uri): void {} + + delete(uri: vscode.Uri): void { + // Delete is not supported in this implementation + } + + rename(oldUri: vscode.Uri, newUri: vscode.Uri, options: { readonly overwrite: boolean; }): void { + // Rename is not supported in this implementation + } + + // Method to get file content as string + getFileContent(uri: vscode.Uri): string { + const filePath = uri.path; + const content = this.fileContentMap[filePath]; + return content ? Buffer.from(content).toString('utf8') : ''; + } +} From b9d73010019f489a48ff8a36bbbc875c40c491ff Mon Sep 17 00:00:00 2001 From: amitjoshi Date: Fri, 22 Nov 2024 15:55:20 +0530 Subject: [PATCH 03/13] Integrate CreateSiteCommand into CommandRegistry and update related components for site creation functionality --- src/common/chat-participants/CommandRegistry.ts | 3 ++- .../powerpages/PowerPagesChatParticipant.ts | 7 +++++-- .../commands/create-site/CreateSiteCommand.ts | 14 +++++++------- .../commands/create-site/CreateSiteHelper.ts | 3 ++- .../commands/create-site/Nl2SiteService.ts | 4 +++- 5 files changed, 19 insertions(+), 12 deletions(-) diff --git a/src/common/chat-participants/CommandRegistry.ts b/src/common/chat-participants/CommandRegistry.ts index ac1281fa..510b4e03 100644 --- a/src/common/chat-participants/CommandRegistry.ts +++ b/src/common/chat-participants/CommandRegistry.ts @@ -4,6 +4,7 @@ */ import * as vscode from "vscode"; +import { CreateSiteCommand } from "./powerpages/commands/create-site/CreateSiteCommand"; export interface Command { // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -11,7 +12,7 @@ export interface Command { } export class CommandRegistry { - private commands: { [key: string]: Command } = {}; + private commands: { [key: string]: Command } = {'create-site': new CreateSiteCommand()}; register(commandName: string, command: Command) { this.commands[commandName] = command; diff --git a/src/common/chat-participants/powerpages/PowerPagesChatParticipant.ts b/src/common/chat-participants/powerpages/PowerPagesChatParticipant.ts index e1586846..9fba1fac 100644 --- a/src/common/chat-participants/powerpages/PowerPagesChatParticipant.ts +++ b/src/common/chat-participants/powerpages/PowerPagesChatParticipant.ts @@ -26,7 +26,7 @@ import { CommandRegistry } from '../CommandRegistry'; import { VSCODE_EXTENSION_GITHUB_POWER_PAGES_AGENT_INVOKED, VSCODE_EXTENSION_GITHUB_POWER_PAGES_AGENT_ORG_DETAILS_NOT_FOUND, VSCODE_EXTENSION_GITHUB_POWER_PAGES_AGENT_ORG_DETAILS, VSCODE_EXTENSION_GITHUB_POWER_PAGES_AGENT_NOT_AVAILABLE_ECS, VSCODE_EXTENSION_GITHUB_POWER_PAGES_AGENT_SUCCESSFUL_PROMPT, VSCODE_EXTENSION_GITHUB_POWER_PAGES_AGENT_WELCOME_PROMPT, VSCODE_EXTENSION_GITHUB_POWER_PAGES_AGENT_NO_PROMPT, VSCODE_EXTENSION_GITHUB_POWER_PAGES_AGENT_LOCATION_REFERENCED, VSCODE_EXTENSION_GITHUB_POWER_PAGES_AGENT_WEBPAGE_RELATED_FILES, VSCODE_EXTENSION_GITHUB_POWER_PAGES_AGENT_SCENARIO, VSCODE_EXTENSION_GITHUB_POWER_PAGES_AGENT_ERROR, VSCODE_EXTENSION_GITHUB_POWER_PAGES_AGENT_COMMAND_TRIGGERED } from './PowerPagesChatParticipantTelemetryConstants'; // Initialize Command Registry and Register Commands -const commandRegistry = new CommandRegistry(); + //Register Commands export class PowerPagesChatParticipant { @@ -98,6 +98,8 @@ export class PowerPagesChatParticipant { this.telemetry.sendTelemetryEvent(VSCODE_EXTENSION_GITHUB_POWER_PAGES_AGENT_INVOKED, { sessionId: this.powerPagesAgentSessionId }); oneDSLoggerWrapper.getLogger().traceInfo(VSCODE_EXTENSION_GITHUB_POWER_PAGES_AGENT_INVOKED, { sessionId: this.powerPagesAgentSessionId }); + const commandRegistry = new CommandRegistry(); + if (!this.isOrgDetailsInitialized) { stream.progress(PAC_AUTH_INPUT); await this.initializeOrgDetails(); @@ -174,7 +176,8 @@ export class PowerPagesChatParticipant { telemetry: this.telemetry, orgID: this.orgID, envID: this.environmentID, - userId: userId + userId: userId, + extensionContext: this.extensionContext }; return await command.execute(commandRequest, stream); diff --git a/src/common/chat-participants/powerpages/commands/create-site/CreateSiteCommand.ts b/src/common/chat-participants/powerpages/commands/create-site/CreateSiteCommand.ts index 5d3f5de6..d49c6f34 100644 --- a/src/common/chat-participants/powerpages/commands/create-site/CreateSiteCommand.ts +++ b/src/common/chat-participants/powerpages/commands/create-site/CreateSiteCommand.ts @@ -12,8 +12,8 @@ import { VSCODE_EXTENSION_CREATE_SITE_COMMAND_FAILED} from "../../PowerPagesChat export class CreateSiteCommand implements Command { // eslint-disable-next-line @typescript-eslint/no-explicit-any - async execute(request: any, stream: vscode.ChatResponseStream): Promise { - const { prompt, intelligenceAPIEndpointInfo, intelligenceApiToken, powerPagesAgentSessionId, telemetry, orgId, envId, userId, extensionContext } = request; + async execute(requestObject: any, stream: vscode.ChatResponseStream): Promise { + const { request, intelligenceAPIEndpointInfo, intelligenceApiToken, powerPagesAgentSessionId, telemetry, orgID, envID, userId, extensionContext } = requestObject; stream.progress(NL2SITE_GENERATING_SITE); try { @@ -21,12 +21,12 @@ export class CreateSiteCommand implements Command { const result = await createSite( intelligenceAPIEndpointInfo.intelligenceEndpoint, intelligenceApiToken, - prompt, + request.prompt, powerPagesAgentSessionId, stream, telemetry, - orgId, - envId, + orgID, + envID, userId, extensionContext ); @@ -39,8 +39,8 @@ export class CreateSiteCommand implements Command { }; } catch (error) { stream.markdown(FAILED_TO_CREATE_SITE); - telemetry.sendTelemetryEvent(VSCODE_EXTENSION_CREATE_SITE_COMMAND_FAILED, { sessionId: powerPagesAgentSessionId, orgId:orgId, envId: envId, userId: userId, error: error as string }); - oneDSLoggerWrapper.getLogger().traceError(VSCODE_EXTENSION_CREATE_SITE_COMMAND_FAILED, error as string, error as Error, { sessionId: powerPagesAgentSessionId, orgId:orgId, envId: envId, userId: userId}, {}); + telemetry.sendTelemetryEvent(VSCODE_EXTENSION_CREATE_SITE_COMMAND_FAILED, { sessionId: powerPagesAgentSessionId, orgId:orgID, envId: envID, userId: userId, error: error as string }); + oneDSLoggerWrapper.getLogger().traceError(VSCODE_EXTENSION_CREATE_SITE_COMMAND_FAILED, error as string, error as Error, { sessionId: powerPagesAgentSessionId, orgId:orgID, envId: envID, userId: userId}, {}); return { metadata: { command: '' diff --git a/src/common/chat-participants/powerpages/commands/create-site/CreateSiteHelper.ts b/src/common/chat-participants/powerpages/commands/create-site/CreateSiteHelper.ts index b96676aa..e886fd5a 100644 --- a/src/common/chat-participants/powerpages/commands/create-site/CreateSiteHelper.ts +++ b/src/common/chat-participants/powerpages/commands/create-site/CreateSiteHelper.ts @@ -14,11 +14,12 @@ import { VSCODE_EXTENSION_NL2PAGE_REQUEST, VSCODE_EXTENSION_NL2SITE_REQUEST } fr import { EditableFileSystemProvider } from '../../../../utilities/EditableFileSystemProvider'; export const createSite = async (intelligenceEndpoint: string, intelligenceApiToken: string, userPrompt: string, sessionId: string, stream: vscode.ChatResponseStream, telemetry: ITelemetry, orgId: string, envID: string, userId: string, extensionContext: vscode.ExtensionContext) => { - const { siteName, siteDescription, sitePages} = await fetchSiteAndPageData(intelligenceEndpoint, intelligenceApiToken, userPrompt, sessionId, telemetry, stream, orgId, envID, userId); + const { siteName, siteDescription, sitePages } = await fetchSiteAndPageData(intelligenceEndpoint, intelligenceApiToken, userPrompt, sessionId, telemetry, stream, orgId, envID, userId); const contentProvider = previewSitePagesContent(siteName, sitePages, stream, extensionContext); stream.button({ + //command: 'create-site-inputs', title: 'Create Site', command: 'create-site-command', arguments: [contentProvider, sitePages.map(page => ({ name: page.metadata.pageTitle, content: page.code }))] diff --git a/src/common/chat-participants/powerpages/commands/create-site/Nl2SiteService.ts b/src/common/chat-participants/powerpages/commands/create-site/Nl2SiteService.ts index d13716af..2bbf241a 100644 --- a/src/common/chat-participants/powerpages/commands/create-site/Nl2SiteService.ts +++ b/src/common/chat-participants/powerpages/commands/create-site/Nl2SiteService.ts @@ -23,7 +23,9 @@ export async function getNL2SiteData(aibEndpoint: string, aibToken: string, user "version": "V1", "information": { "minPages": 7, - "maxPages": 7 + "maxPages": 7, + "language": "English" + } } }; From 8b32d097f97f210f608367c21b5cc7c1cd7ab5bd Mon Sep 17 00:00:00 2001 From: amitjoshi Date: Fri, 22 Nov 2024 16:03:50 +0530 Subject: [PATCH 04/13] Disable copy functionality in EditableFileSystemProvider implementation --- src/common/utilities/EditableFileSystemProvider.ts | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/common/utilities/EditableFileSystemProvider.ts b/src/common/utilities/EditableFileSystemProvider.ts index 9320a990..6f06851c 100644 --- a/src/common/utilities/EditableFileSystemProvider.ts +++ b/src/common/utilities/EditableFileSystemProvider.ts @@ -20,11 +20,7 @@ export class EditableFileSystemProvider implements vscode.FileSystemProvider { } copy(source: vscode.Uri, destination: vscode.Uri, options: { readonly overwrite: boolean; }): void | Thenable { - if (!options.overwrite && this.fileContentMap[destination.path]) { - throw vscode.FileSystemError.FileExists(destination); - } - this.fileContentMap[destination.path] = this.fileContentMap[source.path]; - this._onDidChangeEmitter.fire([{ type: vscode.FileChangeType.Created, uri: destination }]); + // Copy is not supported in this implementation } // Read file content From 2636dcd836e4fbea8e70215bfa0b7577be021d9c Mon Sep 17 00:00:00 2001 From: amitjoshi Date: Fri, 22 Nov 2024 16:34:13 +0530 Subject: [PATCH 05/13] Remove ReadonlyFileSystemProvider implementation --- .../utilities/ReadonlyFileSystemProvider.ts | 27 ------------------- 1 file changed, 27 deletions(-) delete mode 100644 src/common/utilities/ReadonlyFileSystemProvider.ts diff --git a/src/common/utilities/ReadonlyFileSystemProvider.ts b/src/common/utilities/ReadonlyFileSystemProvider.ts deleted file mode 100644 index 2da1d7a4..00000000 --- a/src/common/utilities/ReadonlyFileSystemProvider.ts +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - */ - -import * as vscode from 'vscode'; - -export class ReadonlyFileSystemProvider implements vscode.TextDocumentContentProvider { - // File content mapping - private fileContentMap: { [key: string]: string } = {}; - - // Provide content for a given URI - provideTextDocumentContent(uri: vscode.Uri): string { - const filePath = uri.path; - return this.fileContentMap[filePath] || 'File not found'; - } - - // Update content for a given file - updateFileContent(filePath: string, content: string) { - this.fileContentMap[filePath] = content; - const uri = vscode.Uri.parse(`readonly:${filePath}`); - this._onDidChangeEmitter.fire(uri); - } - - private _onDidChangeEmitter = new vscode.EventEmitter(); - readonly onDidChange = this._onDidChangeEmitter.event; -} From 7b26d081ec50beb4175e87ffa630fe1cd1ae446f Mon Sep 17 00:00:00 2001 From: amitjoshi Date: Fri, 22 Nov 2024 17:01:16 +0530 Subject: [PATCH 06/13] Add telemetry constant for previewing site pages and refactor related components --- ...rPagesChatParticipantTelemetryConstants.ts | 1 + .../create-site/CreateSiteConstants.ts | 6 +++ .../commands/create-site/CreateSiteHelper.ts | 54 +++++++------------ src/common/constants.ts | 2 + 4 files changed, 29 insertions(+), 34 deletions(-) create mode 100644 src/common/chat-participants/powerpages/commands/create-site/CreateSiteConstants.ts diff --git a/src/common/chat-participants/powerpages/PowerPagesChatParticipantTelemetryConstants.ts b/src/common/chat-participants/powerpages/PowerPagesChatParticipantTelemetryConstants.ts index 0f685709..45392515 100644 --- a/src/common/chat-participants/powerpages/PowerPagesChatParticipantTelemetryConstants.ts +++ b/src/common/chat-participants/powerpages/PowerPagesChatParticipantTelemetryConstants.ts @@ -25,3 +25,4 @@ export const VSCODE_EXTENSION_CREATE_SITE_COMMAND_FAILED = 'VSCodeExtensionNL2Si export const VSCODE_EXTENSION_GITHUB_POWER_PAGES_AGENT_COMMAND_TRIGGERED = 'VSCodeExtensionGitHubPowerPagesAgentCommandTriggered'; export const VSCODE_EXTENSION_NL2PAGE_REQUEST = 'VSCodeExtensionNL2PageRequest'; export const VSCODE_EXTENSION_NL2SITE_REQUEST = 'VSCodeExtensionNL2SiteRequest'; +export const VSCODE_EXTENSION_PREVIEW_SITE_PAGES = 'VSCodeExtensionPreviewSitePages'; diff --git a/src/common/chat-participants/powerpages/commands/create-site/CreateSiteConstants.ts b/src/common/chat-participants/powerpages/commands/create-site/CreateSiteConstants.ts new file mode 100644 index 00000000..750afbd4 --- /dev/null +++ b/src/common/chat-participants/powerpages/commands/create-site/CreateSiteConstants.ts @@ -0,0 +1,6 @@ +/* + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + */ + +export const EDITABLE_SCHEME = 'editable'; diff --git a/src/common/chat-participants/powerpages/commands/create-site/CreateSiteHelper.ts b/src/common/chat-participants/powerpages/commands/create-site/CreateSiteHelper.ts index e886fd5a..93f068d3 100644 --- a/src/common/chat-participants/powerpages/commands/create-site/CreateSiteHelper.ts +++ b/src/common/chat-participants/powerpages/commands/create-site/CreateSiteHelper.ts @@ -9,35 +9,18 @@ import { getNL2PageData } from './Nl2PageService'; import { getNL2SiteData } from './Nl2SiteService'; import { NL2SITE_REQUEST_FAILED, NL2PAGE_GENERATING_WEBPAGES, NL2PAGE_RESPONSE_FAILED } from '../../PowerPagesChatParticipantConstants'; import { oneDSLoggerWrapper } from '../../../../OneDSLoggerTelemetry/oneDSLoggerWrapper'; -import { VSCODE_EXTENSION_NL2PAGE_REQUEST, VSCODE_EXTENSION_NL2SITE_REQUEST } from '../../PowerPagesChatParticipantTelemetryConstants'; -//import { ReadonlyFileSystemProvider } from '../../../../utilities/ReadonlyFileSystemProvider'; +import { VSCODE_EXTENSION_NL2PAGE_REQUEST, VSCODE_EXTENSION_NL2SITE_REQUEST, VSCODE_EXTENSION_PREVIEW_SITE_PAGES } from '../../PowerPagesChatParticipantTelemetryConstants'; import { EditableFileSystemProvider } from '../../../../utilities/EditableFileSystemProvider'; +import { HTML_FILE_EXTENSION, UTF8_ENCODING } from '../../../../constants'; +import { EDITABLE_SCHEME } from './CreateSiteConstants'; export const createSite = async (intelligenceEndpoint: string, intelligenceApiToken: string, userPrompt: string, sessionId: string, stream: vscode.ChatResponseStream, telemetry: ITelemetry, orgId: string, envID: string, userId: string, extensionContext: vscode.ExtensionContext) => { const { siteName, siteDescription, sitePages } = await fetchSiteAndPageData(intelligenceEndpoint, intelligenceApiToken, userPrompt, sessionId, telemetry, stream, orgId, envID, userId); - const contentProvider = previewSitePagesContent(siteName, sitePages, stream, extensionContext); + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const contentProvider = previewSitePagesContent(siteName, sitePages, stream, extensionContext, telemetry, sessionId, orgId, envID, userId); - stream.button({ - //command: 'create-site-inputs', - title: 'Create Site', - command: 'create-site-command', - arguments: [contentProvider, sitePages.map(page => ({ name: page.metadata.pageTitle, content: page.code }))] - }); - - extensionContext.subscriptions.push( - vscode.commands.registerCommand('create-site-command', async (contentProvider: EditableFileSystemProvider, sitePagesContent: { name: string; content: string }[]) => { - const updatedPages = sitePagesContent.map(page => ({ - name: page.name, - content: getUpdatedPageContent(contentProvider, page.name) - })); - - // Process the updated pages as needed - console.log('Updated Pages:', updatedPages); - - // You can add further logic here to handle the updated pages, such as sending them to a server or saving them. - }) - ); + // TODO: Implement the create site button click handler return { siteName, @@ -77,32 +60,35 @@ function previewSitePagesContent( siteName: string, sitePages: any[], stream: vscode.ChatResponseStream, - extensionContext: vscode.ExtensionContext + extensionContext: vscode.ExtensionContext, + telemetry: ITelemetry, + sessionId: string, + orgId: string, + envId: string, + userId: string ): EditableFileSystemProvider { const sitePagesContent: { name: string; content: string }[] = []; sitePages.forEach((page: any) => { sitePagesContent.push({ name: page.metadata.pageTitle, content: page.code }); }); - stream.markdown('\nHere is the name of the site: ' + siteName); - const sitePagesFolder: vscode.ChatResponseFileTree[] = []; const contentProvider = new EditableFileSystemProvider(); - const scheme = 'editable'; // Register the content provider extensionContext.subscriptions.push( - vscode.workspace.registerFileSystemProvider(scheme, contentProvider, { isCaseSensitive: true }) + vscode.workspace.registerFileSystemProvider(EDITABLE_SCHEME, contentProvider, { isCaseSensitive: true }) ); - const baseUri = vscode.Uri.parse('editable:/'); + const baseUri = vscode.Uri.parse(`${EDITABLE_SCHEME}:/`); sitePagesContent.forEach((page: { name: string; content: string; }) => { - sitePagesFolder.push({ name: page.name + '.html' }); - const pageUri = vscode.Uri.joinPath(baseUri, page.name + '.html'); - contentProvider.writeFile(pageUri, Buffer.from(page.content, 'utf8')); + sitePagesFolder.push({ name: page.name + HTML_FILE_EXTENSION }); + const pageUri = vscode.Uri.joinPath(baseUri, page.name + HTML_FILE_EXTENSION); + contentProvider.writeFile(pageUri, Buffer.from(page.content, UTF8_ENCODING)); }); - // TODO: pass uri of current workspace as second parameter + telemetry.sendTelemetryEvent(VSCODE_EXTENSION_PREVIEW_SITE_PAGES, {sessionId: sessionId, orgId: orgId, environmentId: envId, userId: userId}); + stream.filetree(sitePagesFolder, baseUri); return contentProvider; @@ -110,7 +96,7 @@ function previewSitePagesContent( // Function to get updated content export function getUpdatedPageContent(contentProvider: EditableFileSystemProvider, pageName: string): string { - const pageUri = vscode.Uri.parse(`editable:/${pageName}.html`); + const pageUri = vscode.Uri.parse(`${EDITABLE_SCHEME}:/${pageName}${HTML_FILE_EXTENSION}`); return contentProvider.getFileContent(pageUri); } diff --git a/src/common/constants.ts b/src/common/constants.ts index e9984293..dfb5f8fb 100644 --- a/src/common/constants.ts +++ b/src/common/constants.ts @@ -87,3 +87,5 @@ export interface IApiRequestParams { export const VSCODE_EXTENSION_COPILOT_CONTEXT_RELATED_FILES_FETCH_FAILED = "VSCodeExtensionCopilotContextRelatedFilesFetchFailed"; export const ADX_WEBPAGE = 'adx_webpage' +export const HTML_FILE_EXTENSION = '.html'; +export const UTF8_ENCODING = 'utf8'; From 66240c4e624fa5ece7858cb5f8191f7762fe6264 Mon Sep 17 00:00:00 2001 From: amitjoshi Date: Fri, 22 Nov 2024 17:27:27 +0530 Subject: [PATCH 07/13] Refactor CommandRegistry and add command registration utility for chat participants --- src/common/chat-participants/ChatParticipantUtils.ts | 7 +++++++ src/common/chat-participants/CommandRegistry.ts | 3 +-- .../powerpages/PowerPagesChatParticipant.ts | 10 ++++------ .../powerpages/PowerPagesChatParticipantConstants.ts | 4 ++++ 4 files changed, 16 insertions(+), 8 deletions(-) diff --git a/src/common/chat-participants/ChatParticipantUtils.ts b/src/common/chat-participants/ChatParticipantUtils.ts index 6b75cbcf..73221b3d 100644 --- a/src/common/chat-participants/ChatParticipantUtils.ts +++ b/src/common/chat-participants/ChatParticipantUtils.ts @@ -4,7 +4,14 @@ */ import * as vscode from 'vscode'; +import { Command, CommandRegistry } from './CommandRegistry'; export function createChatParticipant(participantId: string, handler: vscode.ChatRequestHandler): vscode.ChatParticipant { return vscode.chat.createChatParticipant(participantId, handler); } + +export function registerCommands(commandRegistry: CommandRegistry, commands: { [key: string]: Command }) { + for (const commandName in commands) { + commandRegistry.register(commandName, commands[commandName]); + } +} diff --git a/src/common/chat-participants/CommandRegistry.ts b/src/common/chat-participants/CommandRegistry.ts index 510b4e03..ac1281fa 100644 --- a/src/common/chat-participants/CommandRegistry.ts +++ b/src/common/chat-participants/CommandRegistry.ts @@ -4,7 +4,6 @@ */ import * as vscode from "vscode"; -import { CreateSiteCommand } from "./powerpages/commands/create-site/CreateSiteCommand"; export interface Command { // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -12,7 +11,7 @@ export interface Command { } export class CommandRegistry { - private commands: { [key: string]: Command } = {'create-site': new CreateSiteCommand()}; + private commands: { [key: string]: Command } = {}; register(commandName: string, command: Command) { this.commands[commandName] = command; diff --git a/src/common/chat-participants/powerpages/PowerPagesChatParticipant.ts b/src/common/chat-participants/powerpages/PowerPagesChatParticipant.ts index 9fba1fac..f7a15077 100644 --- a/src/common/chat-participants/powerpages/PowerPagesChatParticipant.ts +++ b/src/common/chat-participants/powerpages/PowerPagesChatParticipant.ts @@ -4,7 +4,7 @@ */ import * as vscode from 'vscode'; -import { createChatParticipant } from '../ChatParticipantUtils'; +import { createChatParticipant, registerCommands } from '../ChatParticipantUtils'; import { IComponentInfo, IPowerPagesChatResult } from './PowerPagesChatParticipantTypes'; import { ITelemetry } from "../../OneDSLoggerTelemetry/telemetry/ITelemetry"; import TelemetryReporter from '@vscode/extension-telemetry'; @@ -12,7 +12,7 @@ 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, COPILOT_NOT_RELEASED_MSG, DISCLAIMER_MESSAGE, INVALID_RESPONSE, NO_PROMPT_MESSAGE, PAC_AUTH_INPUT, PAC_AUTH_NOT_FOUND, POWERPAGES_CHAT_PARTICIPANT_ID, RESPONSE_AWAITED_MSG, RESPONSE_SCENARIOS, SKIP_CODES, STATER_PROMPTS, WELCOME_MESSAGE, WELCOME_PROMPT } from './PowerPagesChatParticipantConstants'; +import { AUTHENTICATION_FAILED_MSG, COPILOT_NOT_AVAILABLE_MSG, COPILOT_NOT_RELEASED_MSG, DISCLAIMER_MESSAGE, INVALID_RESPONSE, NO_PROMPT_MESSAGE, PAC_AUTH_INPUT, PAC_AUTH_NOT_FOUND, POWERPAGES_CHAT_PARTICIPANT_ID, POWERPAGES_COMMANDS, RESPONSE_AWAITED_MSG, RESPONSE_SCENARIOS, SKIP_CODES, STATER_PROMPTS, WELCOME_MESSAGE, WELCOME_PROMPT } from './PowerPagesChatParticipantConstants'; import { ORG_DETAILS_KEY, handleOrgChangeSuccess, initializeOrgDetails } from '../../utilities/OrgHandlerUtils'; import { createAndReferenceLocation, getComponentInfo, getEndpoint, provideChatParticipantFollowups, handleChatParticipantFeedback, createErrorResult, createSuccessResult, removeChatVariables } from './PowerPagesChatParticipantUtils'; import { checkCopilotAvailability, fetchRelatedFiles, getActiveEditorContent } from '../../utilities/Utils'; @@ -25,10 +25,6 @@ import { oneDSLoggerWrapper } from '../../OneDSLoggerTelemetry/oneDSLoggerWrappe import { CommandRegistry } from '../CommandRegistry'; import { VSCODE_EXTENSION_GITHUB_POWER_PAGES_AGENT_INVOKED, VSCODE_EXTENSION_GITHUB_POWER_PAGES_AGENT_ORG_DETAILS_NOT_FOUND, VSCODE_EXTENSION_GITHUB_POWER_PAGES_AGENT_ORG_DETAILS, VSCODE_EXTENSION_GITHUB_POWER_PAGES_AGENT_NOT_AVAILABLE_ECS, VSCODE_EXTENSION_GITHUB_POWER_PAGES_AGENT_SUCCESSFUL_PROMPT, VSCODE_EXTENSION_GITHUB_POWER_PAGES_AGENT_WELCOME_PROMPT, VSCODE_EXTENSION_GITHUB_POWER_PAGES_AGENT_NO_PROMPT, VSCODE_EXTENSION_GITHUB_POWER_PAGES_AGENT_LOCATION_REFERENCED, VSCODE_EXTENSION_GITHUB_POWER_PAGES_AGENT_WEBPAGE_RELATED_FILES, VSCODE_EXTENSION_GITHUB_POWER_PAGES_AGENT_SCENARIO, VSCODE_EXTENSION_GITHUB_POWER_PAGES_AGENT_ERROR, VSCODE_EXTENSION_GITHUB_POWER_PAGES_AGENT_COMMAND_TRIGGERED } from './PowerPagesChatParticipantTelemetryConstants'; -// Initialize Command Registry and Register Commands - -//Register Commands - export class PowerPagesChatParticipant { private static instance: PowerPagesChatParticipant | null = null; private chatParticipant: vscode.ChatParticipant; @@ -100,6 +96,8 @@ export class PowerPagesChatParticipant { const commandRegistry = new CommandRegistry(); + registerCommands(commandRegistry, POWERPAGES_COMMANDS); + if (!this.isOrgDetailsInitialized) { stream.progress(PAC_AUTH_INPUT); await this.initializeOrgDetails(); diff --git a/src/common/chat-participants/powerpages/PowerPagesChatParticipantConstants.ts b/src/common/chat-participants/powerpages/PowerPagesChatParticipantConstants.ts index 5acd6fb3..8b5d475c 100644 --- a/src/common/chat-participants/powerpages/PowerPagesChatParticipantConstants.ts +++ b/src/common/chat-participants/powerpages/PowerPagesChatParticipantConstants.ts @@ -5,6 +5,7 @@ import * as vscode from 'vscode'; import { ADX_ENTITYFORM, ADX_ENTITYLIST } from '../../copilot/constants'; +import { CreateSiteCommand } from './commands/create-site/CreateSiteCommand'; // Constants export const POWERPAGES_CHAT_PARTICIPANT_ID = 'powerpages'; @@ -58,3 +59,6 @@ export const NL2PAGE_GENERATING_WEBPAGES = vscode.l10n.t("Generating webpages... export const NL2PAGE_RESPONSE_FAILED = 'Failed to get page content from NL2Page service'; export const NL2SITE_GENERATING_SITE = vscode.l10n.t("Generating a new Power Pages site..."); export const FAILED_TO_CREATE_SITE = vscode.l10n.t('Failed to create a new Power Pages site. Please try again.'); +export const POWERPAGES_COMMANDS = { + 'create-site': new CreateSiteCommand() +} From 255c97e2bfa0f6dcf3dcd5b9fba6ce65507b9445 Mon Sep 17 00:00:00 2001 From: amitjoshi Date: Fri, 22 Nov 2024 17:32:12 +0530 Subject: [PATCH 08/13] Add constants for site creation parameters and refactor NL2SiteService to use them --- .../powerpages/commands/create-site/CreateSiteConstants.ts | 3 +++ .../powerpages/commands/create-site/Nl2SiteService.ts | 7 ++++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/common/chat-participants/powerpages/commands/create-site/CreateSiteConstants.ts b/src/common/chat-participants/powerpages/commands/create-site/CreateSiteConstants.ts index 750afbd4..eac8c582 100644 --- a/src/common/chat-participants/powerpages/commands/create-site/CreateSiteConstants.ts +++ b/src/common/chat-participants/powerpages/commands/create-site/CreateSiteConstants.ts @@ -4,3 +4,6 @@ */ export const EDITABLE_SCHEME = 'editable'; +export const ENGLISH = "English"; +export const MIN_PAGES = 7; +export const MAX_PAGES = 7; diff --git a/src/common/chat-participants/powerpages/commands/create-site/Nl2SiteService.ts b/src/common/chat-participants/powerpages/commands/create-site/Nl2SiteService.ts index 2bbf241a..0c9af94d 100644 --- a/src/common/chat-participants/powerpages/commands/create-site/Nl2SiteService.ts +++ b/src/common/chat-participants/powerpages/commands/create-site/Nl2SiteService.ts @@ -8,6 +8,7 @@ import { NL2SITE_GENERATE_NEW_SITE, NL2SITE_INVALID_RESPONSE, NL2SITE_SCENARIO} import {VSCODE_EXTENSION_NL2SITE_REQUEST_FAILED, VSCODE_EXTENSION_NL2SITE_REQUEST_SUCCESS } from "../../PowerPagesChatParticipantTelemetryConstants"; import { getCommonHeaders } from "../../../../services/AuthenticationProvider"; import { oneDSLoggerWrapper } from "../../../../OneDSLoggerTelemetry/oneDSLoggerWrapper"; +import { ENGLISH, MAX_PAGES, MIN_PAGES } from "./CreateSiteConstants"; export async function getNL2SiteData(aibEndpoint: string, aibToken: string, userPrompt: string, sessionId: string, telemetry: ITelemetry, orgId: string, envId: string, userId: string) { const requestBody = { @@ -22,9 +23,9 @@ export async function getNL2SiteData(aibEndpoint: string, aibToken: string, user // "shouldCheckBlockList": false, //TODO: Check if this is needed "version": "V1", "information": { - "minPages": 7, - "maxPages": 7, - "language": "English" + "minPages": MIN_PAGES, + "maxPages": MAX_PAGES, + "language": ENGLISH } } From 765362235e1764ab3033a993bd1f4633ddc7d4b0 Mon Sep 17 00:00:00 2001 From: amitjoshi Date: Mon, 25 Nov 2024 17:27:15 +0530 Subject: [PATCH 09/13] Refactor CreateSiteCommand and CreateSiteHelper to use structured options and improve readability; add CreateSiteTypes for better type management --- .../commands/create-site/CreateSiteCommand.ts | 14 +++---- .../commands/create-site/CreateSiteHelper.ts | 42 +++++++++++++------ .../commands/create-site/CreateSiteTypes.ts | 32 ++++++++++++++ 3 files changed, 68 insertions(+), 20 deletions(-) create mode 100644 src/common/chat-participants/powerpages/commands/create-site/CreateSiteTypes.ts diff --git a/src/common/chat-participants/powerpages/commands/create-site/CreateSiteCommand.ts b/src/common/chat-participants/powerpages/commands/create-site/CreateSiteCommand.ts index d49c6f34..f6212e79 100644 --- a/src/common/chat-participants/powerpages/commands/create-site/CreateSiteCommand.ts +++ b/src/common/chat-participants/powerpages/commands/create-site/CreateSiteCommand.ts @@ -18,18 +18,18 @@ export class CreateSiteCommand implements Command { stream.progress(NL2SITE_GENERATING_SITE); try { // eslint-disable-next-line @typescript-eslint/no-unused-vars - const result = await createSite( - intelligenceAPIEndpointInfo.intelligenceEndpoint, + const result = await createSite({ + intelligenceEndpoint: intelligenceAPIEndpointInfo.intelligenceEndpoint, intelligenceApiToken, - request.prompt, - powerPagesAgentSessionId, + userPrompt: request.prompt, + sessionId: powerPagesAgentSessionId, stream, telemetry, - orgID, - envID, + orgId: orgID, + envId: envID, userId, extensionContext - ); + }); // Process the result return { diff --git a/src/common/chat-participants/powerpages/commands/create-site/CreateSiteHelper.ts b/src/common/chat-participants/powerpages/commands/create-site/CreateSiteHelper.ts index 93f068d3..cebd6150 100644 --- a/src/common/chat-participants/powerpages/commands/create-site/CreateSiteHelper.ts +++ b/src/common/chat-participants/powerpages/commands/create-site/CreateSiteHelper.ts @@ -13,12 +13,26 @@ import { VSCODE_EXTENSION_NL2PAGE_REQUEST, VSCODE_EXTENSION_NL2SITE_REQUEST, VSC import { EditableFileSystemProvider } from '../../../../utilities/EditableFileSystemProvider'; import { HTML_FILE_EXTENSION, UTF8_ENCODING } from '../../../../constants'; import { EDITABLE_SCHEME } from './CreateSiteConstants'; - -export const createSite = async (intelligenceEndpoint: string, intelligenceApiToken: string, userPrompt: string, sessionId: string, stream: vscode.ChatResponseStream, telemetry: ITelemetry, orgId: string, envID: string, userId: string, extensionContext: vscode.ExtensionContext) => { - const { siteName, siteDescription, sitePages } = await fetchSiteAndPageData(intelligenceEndpoint, intelligenceApiToken, userPrompt, sessionId, telemetry, stream, orgId, envID, userId); +import { ICreateSiteOptions, IPreviewSitePagesContentOptions } from './CreateSiteTypes'; + +export const createSite = async (createSiteOptions: ICreateSiteOptions) => { + const { + intelligenceEndpoint, + intelligenceApiToken, + userPrompt, + sessionId, + stream, + telemetry, + orgId, + envId, + userId, + extensionContext + } = createSiteOptions; + + const { siteName, siteDescription, sitePages } = await fetchSiteAndPageData(intelligenceEndpoint, intelligenceApiToken, userPrompt, sessionId, telemetry, stream, orgId, envId, userId); // eslint-disable-next-line @typescript-eslint/no-unused-vars - const contentProvider = previewSitePagesContent(siteName, sitePages, stream, extensionContext, telemetry, sessionId, orgId, envID, userId); + const contentProvider = previewSitePagesContent({sitePages, stream, extensionContext, telemetry, sessionId, orgId, envId, userId}); // TODO: Implement the create site button click handler @@ -57,16 +71,18 @@ async function fetchSiteAndPageData(intelligenceEndpoint: string, intelligenceAp function previewSitePagesContent( - siteName: string, - sitePages: any[], - stream: vscode.ChatResponseStream, - extensionContext: vscode.ExtensionContext, - telemetry: ITelemetry, - sessionId: string, - orgId: string, - envId: string, - userId: string + options: IPreviewSitePagesContentOptions ): EditableFileSystemProvider { + const { + sitePages, + stream, + extensionContext, + telemetry, + sessionId, + orgId, + envId, + userId + } = options; const sitePagesContent: { name: string; content: string }[] = []; sitePages.forEach((page: any) => { sitePagesContent.push({ name: page.metadata.pageTitle, content: page.code }); diff --git a/src/common/chat-participants/powerpages/commands/create-site/CreateSiteTypes.ts b/src/common/chat-participants/powerpages/commands/create-site/CreateSiteTypes.ts new file mode 100644 index 00000000..505e5c5f --- /dev/null +++ b/src/common/chat-participants/powerpages/commands/create-site/CreateSiteTypes.ts @@ -0,0 +1,32 @@ +/* + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + */ + +import { ITelemetry } from "../../../../OneDSLoggerTelemetry/telemetry/ITelemetry"; +import * as vscode from 'vscode'; + +export interface ICreateSiteOptions { + intelligenceEndpoint: string; + intelligenceApiToken: string; + userPrompt: string; + sessionId: string; + stream: vscode.ChatResponseStream; + telemetry: ITelemetry; + orgId: string; + envId: string; + userId: string; + extensionContext: vscode.ExtensionContext; +} + +export interface IPreviewSitePagesContentOptions { + // siteName: string; + sitePages: any[]; + stream: vscode.ChatResponseStream; + extensionContext: vscode.ExtensionContext; + telemetry: ITelemetry; + sessionId: string; + orgId: string; + envId: string; + userId: string; +} From 537414c4e9599ec9c5022ee46d847519198e4c10 Mon Sep 17 00:00:00 2001 From: amitjoshi Date: Tue, 26 Nov 2024 15:25:24 +0530 Subject: [PATCH 10/13] Add error telemetry constant for previewing site pages and handle errors in previewSitePagesContent function --- ...rPagesChatParticipantTelemetryConstants.ts | 1 + .../commands/create-site/CreateSiteHelper.ts | 50 +++++++++++-------- 2 files changed, 29 insertions(+), 22 deletions(-) diff --git a/src/common/chat-participants/powerpages/PowerPagesChatParticipantTelemetryConstants.ts b/src/common/chat-participants/powerpages/PowerPagesChatParticipantTelemetryConstants.ts index 45392515..8138f979 100644 --- a/src/common/chat-participants/powerpages/PowerPagesChatParticipantTelemetryConstants.ts +++ b/src/common/chat-participants/powerpages/PowerPagesChatParticipantTelemetryConstants.ts @@ -26,3 +26,4 @@ export const VSCODE_EXTENSION_GITHUB_POWER_PAGES_AGENT_COMMAND_TRIGGERED = 'VSCo export const VSCODE_EXTENSION_NL2PAGE_REQUEST = 'VSCodeExtensionNL2PageRequest'; export const VSCODE_EXTENSION_NL2SITE_REQUEST = 'VSCodeExtensionNL2SiteRequest'; export const VSCODE_EXTENSION_PREVIEW_SITE_PAGES = 'VSCodeExtensionPreviewSitePages'; +export const VSCODE_EXTENSION_PREVIEW_SITE_PAGES_ERROR = 'VSCodeExtensionPreviewSitePagesError'; diff --git a/src/common/chat-participants/powerpages/commands/create-site/CreateSiteHelper.ts b/src/common/chat-participants/powerpages/commands/create-site/CreateSiteHelper.ts index cebd6150..a9b63bad 100644 --- a/src/common/chat-participants/powerpages/commands/create-site/CreateSiteHelper.ts +++ b/src/common/chat-participants/powerpages/commands/create-site/CreateSiteHelper.ts @@ -9,7 +9,7 @@ import { getNL2PageData } from './Nl2PageService'; import { getNL2SiteData } from './Nl2SiteService'; import { NL2SITE_REQUEST_FAILED, NL2PAGE_GENERATING_WEBPAGES, NL2PAGE_RESPONSE_FAILED } from '../../PowerPagesChatParticipantConstants'; import { oneDSLoggerWrapper } from '../../../../OneDSLoggerTelemetry/oneDSLoggerWrapper'; -import { VSCODE_EXTENSION_NL2PAGE_REQUEST, VSCODE_EXTENSION_NL2SITE_REQUEST, VSCODE_EXTENSION_PREVIEW_SITE_PAGES } from '../../PowerPagesChatParticipantTelemetryConstants'; +import { VSCODE_EXTENSION_NL2PAGE_REQUEST, VSCODE_EXTENSION_NL2SITE_REQUEST, VSCODE_EXTENSION_PREVIEW_SITE_PAGES, VSCODE_EXTENSION_PREVIEW_SITE_PAGES_ERROR } from '../../PowerPagesChatParticipantTelemetryConstants'; import { EditableFileSystemProvider } from '../../../../utilities/EditableFileSystemProvider'; import { HTML_FILE_EXTENSION, UTF8_ENCODING } from '../../../../constants'; import { EDITABLE_SCHEME } from './CreateSiteConstants'; @@ -71,7 +71,7 @@ async function fetchSiteAndPageData(intelligenceEndpoint: string, intelligenceAp function previewSitePagesContent( - options: IPreviewSitePagesContentOptions + options: IPreviewSitePagesContentOptions ): EditableFileSystemProvider { const { sitePages, @@ -83,33 +83,39 @@ function previewSitePagesContent( envId, userId } = options; - const sitePagesContent: { name: string; content: string }[] = []; - sitePages.forEach((page: any) => { - sitePagesContent.push({ name: page.metadata.pageTitle, content: page.code }); - }); - const sitePagesFolder: vscode.ChatResponseFileTree[] = []; - const contentProvider = new EditableFileSystemProvider(); - // Register the content provider - extensionContext.subscriptions.push( - vscode.workspace.registerFileSystemProvider(EDITABLE_SCHEME, contentProvider, { isCaseSensitive: true }) - ); + try { + const sitePagesContent: { name: string; content: string }[] = []; + sitePages.forEach((page: any) => { + sitePagesContent.push({ name: page.metadata.pageTitle, content: page.code }); + }); - const baseUri = vscode.Uri.parse(`${EDITABLE_SCHEME}:/`); + const sitePagesFolder: vscode.ChatResponseFileTree[] = []; + const contentProvider = new EditableFileSystemProvider(); + // Register the content provider + extensionContext.subscriptions.push( + vscode.workspace.registerFileSystemProvider(EDITABLE_SCHEME, contentProvider, { isCaseSensitive: true }) + ); - sitePagesContent.forEach((page: { name: string; content: string; }) => { - sitePagesFolder.push({ name: page.name + HTML_FILE_EXTENSION }); - const pageUri = vscode.Uri.joinPath(baseUri, page.name + HTML_FILE_EXTENSION); - contentProvider.writeFile(pageUri, Buffer.from(page.content, UTF8_ENCODING)); - }); + const baseUri = vscode.Uri.parse(`${EDITABLE_SCHEME}:/`); - telemetry.sendTelemetryEvent(VSCODE_EXTENSION_PREVIEW_SITE_PAGES, {sessionId: sessionId, orgId: orgId, environmentId: envId, userId: userId}); + sitePagesContent.forEach((page: { name: string; content: string; }) => { + sitePagesFolder.push({ name: page.name + HTML_FILE_EXTENSION }); + const pageUri = vscode.Uri.joinPath(baseUri, page.name + HTML_FILE_EXTENSION); + contentProvider.writeFile(pageUri, Buffer.from(page.content, UTF8_ENCODING)); + }); - stream.filetree(sitePagesFolder, baseUri); + telemetry.sendTelemetryEvent(VSCODE_EXTENSION_PREVIEW_SITE_PAGES, { sessionId, orgId, environmentId: envId, userId }); - return contentProvider; -} + stream.filetree(sitePagesFolder, baseUri); + return contentProvider; + } catch (error) { + telemetry.sendTelemetryEvent(VSCODE_EXTENSION_PREVIEW_SITE_PAGES_ERROR, { sessionId, orgId, environmentId: envId, userId, error: (error as Error).message }); + oneDSLoggerWrapper.getLogger().traceError(VSCODE_EXTENSION_PREVIEW_SITE_PAGES_ERROR, (error as Error).message, error as Error, { sessionId, orgId, environmentId: envId, userId }, {}); + throw error; + } +} // Function to get updated content export function getUpdatedPageContent(contentProvider: EditableFileSystemProvider, pageName: string): string { const pageUri = vscode.Uri.parse(`${EDITABLE_SCHEME}:/${pageName}${HTML_FILE_EXTENSION}`); From 0f73785df20df6a332abf3bbadfb1ad165a09c4a Mon Sep 17 00:00:00 2001 From: amitjoshi Date: Tue, 26 Nov 2024 15:47:18 +0530 Subject: [PATCH 11/13] Rename fileContentMap to _fileContentMap for consistency and clarity in EditableFileSystemProvider --- src/common/utilities/EditableFileSystemProvider.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/common/utilities/EditableFileSystemProvider.ts b/src/common/utilities/EditableFileSystemProvider.ts index 6f06851c..1d486527 100644 --- a/src/common/utilities/EditableFileSystemProvider.ts +++ b/src/common/utilities/EditableFileSystemProvider.ts @@ -9,7 +9,7 @@ import * as vscode from 'vscode'; export class EditableFileSystemProvider implements vscode.FileSystemProvider { - private fileContentMap: { [key: string]: Uint8Array } = {}; + private _fileContentMap: { [key: string]: Uint8Array } = {}; private _onDidChangeEmitter = new vscode.EventEmitter(); readonly onDidChangeFile = this._onDidChangeEmitter.event; @@ -26,19 +26,19 @@ export class EditableFileSystemProvider implements vscode.FileSystemProvider { // Read file content readFile(uri: vscode.Uri): Uint8Array { const filePath = uri.path; - return this.fileContentMap[filePath] || new Uint8Array(); + return this._fileContentMap[filePath] || new Uint8Array(); } // Write file content writeFile(uri: vscode.Uri, content: Uint8Array): void { const filePath = uri.path; - this.fileContentMap[filePath] = content; + this._fileContentMap[filePath] = content; this._onDidChangeEmitter.fire([{ type: vscode.FileChangeType.Changed, uri }]); } // Other required methods for FileSystemProvider stat(uri: vscode.Uri): vscode.FileStat { - return { type: vscode.FileType.File, ctime: Date.now(), mtime: Date.now(), size: this.fileContentMap[uri.path]?.length || 0 }; + return { type: vscode.FileType.File, ctime: Date.now(), mtime: Date.now(), size: this._fileContentMap[uri.path]?.length || 0 }; } readDirectory(uri: vscode.Uri): [string, vscode.FileType][] { @@ -59,7 +59,7 @@ export class EditableFileSystemProvider implements vscode.FileSystemProvider { // Method to get file content as string getFileContent(uri: vscode.Uri): string { const filePath = uri.path; - const content = this.fileContentMap[filePath]; + const content = this._fileContentMap[filePath]; return content ? Buffer.from(content).toString('utf8') : ''; } } From ec005a6cb12c5078727bd3ef2148e6265c83d417 Mon Sep 17 00:00:00 2001 From: amitjoshi Date: Wed, 27 Nov 2024 11:28:25 +0530 Subject: [PATCH 12/13] Remove unused getUpdatedPageContent function from CreateSiteHelper to streamline code --- .../powerpages/commands/create-site/CreateSiteHelper.ts | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/common/chat-participants/powerpages/commands/create-site/CreateSiteHelper.ts b/src/common/chat-participants/powerpages/commands/create-site/CreateSiteHelper.ts index a9b63bad..8bb9546c 100644 --- a/src/common/chat-participants/powerpages/commands/create-site/CreateSiteHelper.ts +++ b/src/common/chat-participants/powerpages/commands/create-site/CreateSiteHelper.ts @@ -116,9 +116,4 @@ function previewSitePagesContent( throw error; } } -// Function to get updated content -export function getUpdatedPageContent(contentProvider: EditableFileSystemProvider, pageName: string): string { - const pageUri = vscode.Uri.parse(`${EDITABLE_SCHEME}:/${pageName}${HTML_FILE_EXTENSION}`); - return contentProvider.getFileContent(pageUri); -} From 15c88427c4a69d6c7aa79c0523d9f60690ed0f20 Mon Sep 17 00:00:00 2001 From: amitjoshi Date: Wed, 27 Nov 2024 17:25:04 +0530 Subject: [PATCH 13/13] Add ESLint disable comments for any type usage in CreateSiteHelper and CreateSiteTypes --- .../powerpages/commands/create-site/CreateSiteHelper.ts | 1 + .../powerpages/commands/create-site/CreateSiteTypes.ts | 1 + 2 files changed, 2 insertions(+) diff --git a/src/common/chat-participants/powerpages/commands/create-site/CreateSiteHelper.ts b/src/common/chat-participants/powerpages/commands/create-site/CreateSiteHelper.ts index 8bb9546c..77d04bc6 100644 --- a/src/common/chat-participants/powerpages/commands/create-site/CreateSiteHelper.ts +++ b/src/common/chat-participants/powerpages/commands/create-site/CreateSiteHelper.ts @@ -86,6 +86,7 @@ function previewSitePagesContent( try { const sitePagesContent: { name: string; content: string }[] = []; + // eslint-disable-next-line @typescript-eslint/no-explicit-any sitePages.forEach((page: any) => { sitePagesContent.push({ name: page.metadata.pageTitle, content: page.code }); }); diff --git a/src/common/chat-participants/powerpages/commands/create-site/CreateSiteTypes.ts b/src/common/chat-participants/powerpages/commands/create-site/CreateSiteTypes.ts index 505e5c5f..30d8f358 100644 --- a/src/common/chat-participants/powerpages/commands/create-site/CreateSiteTypes.ts +++ b/src/common/chat-participants/powerpages/commands/create-site/CreateSiteTypes.ts @@ -21,6 +21,7 @@ export interface ICreateSiteOptions { export interface IPreviewSitePagesContentOptions { // siteName: string; + // eslint-disable-next-line @typescript-eslint/no-explicit-any sitePages: any[]; stream: vscode.ChatResponseStream; extensionContext: vscode.ExtensionContext;