From 8fad2819ee8c1848ade53bc05d2211d08509437c Mon Sep 17 00:00:00 2001 From: amitjoshi438 <54068463+amitjoshi438@users.noreply.github.com> Date: Wed, 27 Nov 2024 18:35:49 +0530 Subject: [PATCH] [PowerPages][create-site] Preview and Edit Site Page and Command Registration (#1061) * Enhance CreateSiteCommand to include extension context and add ReadonlyFileSystemProvider for site page previews * Implement EditableFileSystemProvider for site page editing and update CreateSiteHelper to utilize it * Integrate CreateSiteCommand into CommandRegistry and update related components for site creation functionality * Disable copy functionality in EditableFileSystemProvider implementation * Remove ReadonlyFileSystemProvider implementation * Add telemetry constant for previewing site pages and refactor related components * Refactor CommandRegistry and add command registration utility for chat participants * Add constants for site creation parameters and refactor NL2SiteService to use them * Refactor CreateSiteCommand and CreateSiteHelper to use structured options and improve readability; add CreateSiteTypes for better type management * Add error telemetry constant for previewing site pages and handle errors in previewSitePagesContent function * Rename fileContentMap to _fileContentMap for consistency and clarity in EditableFileSystemProvider * Remove unused getUpdatedPageContent function from CreateSiteHelper to streamline code * Add ESLint disable comments for any type usage in CreateSiteHelper and CreateSiteTypes --------- Co-authored-by: amitjoshi --- .../chat-participants/ChatParticipantUtils.ts | 7 ++ .../powerpages/PowerPagesChatParticipant.ts | 15 ++-- .../PowerPagesChatParticipantConstants.ts | 4 + ...rPagesChatParticipantTelemetryConstants.ts | 2 + .../commands/create-site/CreateSiteCommand.ts | 25 +++--- .../create-site/CreateSiteConstants.ts | 9 +++ .../commands/create-site/CreateSiteHelper.ts | 78 ++++++++++++++++++- .../commands/create-site/CreateSiteTypes.ts | 33 ++++++++ .../commands/create-site/Nl2SiteService.ts | 7 +- src/common/constants.ts | 2 + .../utilities/EditableFileSystemProvider.ts | 65 ++++++++++++++++ 11 files changed, 223 insertions(+), 24 deletions(-) create mode 100644 src/common/chat-participants/powerpages/commands/create-site/CreateSiteConstants.ts create mode 100644 src/common/chat-participants/powerpages/commands/create-site/CreateSiteTypes.ts create mode 100644 src/common/utilities/EditableFileSystemProvider.ts diff --git a/src/common/chat-participants/ChatParticipantUtils.ts b/src/common/chat-participants/ChatParticipantUtils.ts index 6b75cbcfd..73221b3d6 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/powerpages/PowerPagesChatParticipant.ts b/src/common/chat-participants/powerpages/PowerPagesChatParticipant.ts index e15868460..f7a150776 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 -const commandRegistry = new CommandRegistry(); -//Register Commands - export class PowerPagesChatParticipant { private static instance: PowerPagesChatParticipant | null = null; private chatParticipant: vscode.ChatParticipant; @@ -98,6 +94,10 @@ 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(); + + registerCommands(commandRegistry, POWERPAGES_COMMANDS); + if (!this.isOrgDetailsInitialized) { stream.progress(PAC_AUTH_INPUT); await this.initializeOrgDetails(); @@ -174,7 +174,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/PowerPagesChatParticipantConstants.ts b/src/common/chat-participants/powerpages/PowerPagesChatParticipantConstants.ts index 5acd6fb38..8b5d475cc 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() +} diff --git a/src/common/chat-participants/powerpages/PowerPagesChatParticipantTelemetryConstants.ts b/src/common/chat-participants/powerpages/PowerPagesChatParticipantTelemetryConstants.ts index 0f6857094..8138f9795 100644 --- a/src/common/chat-participants/powerpages/PowerPagesChatParticipantTelemetryConstants.ts +++ b/src/common/chat-participants/powerpages/PowerPagesChatParticipantTelemetryConstants.ts @@ -25,3 +25,5 @@ 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'; +export const VSCODE_EXTENSION_PREVIEW_SITE_PAGES_ERROR = 'VSCodeExtensionPreviewSitePagesError'; 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 b2db45a25..f6212e79f 100644 --- a/src/common/chat-participants/powerpages/commands/create-site/CreateSiteCommand.ts +++ b/src/common/chat-participants/powerpages/commands/create-site/CreateSiteCommand.ts @@ -12,23 +12,24 @@ 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; + 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 { // eslint-disable-next-line @typescript-eslint/no-unused-vars - const result = await createSite( - intelligenceAPIEndpointInfo.intelligenceEndpoint, + const result = await createSite({ + intelligenceEndpoint: intelligenceAPIEndpointInfo.intelligenceEndpoint, intelligenceApiToken, - prompt, - powerPagesAgentSessionId, + userPrompt: request.prompt, + sessionId: powerPagesAgentSessionId, stream, telemetry, - orgId, - envId, - userId - ); + orgId: orgID, + envId: envID, + userId, + extensionContext + }); // Process the result return { @@ -38,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/CreateSiteConstants.ts b/src/common/chat-participants/powerpages/commands/create-site/CreateSiteConstants.ts new file mode 100644 index 000000000..eac8c5826 --- /dev/null +++ b/src/common/chat-participants/powerpages/commands/create-site/CreateSiteConstants.ts @@ -0,0 +1,9 @@ +/* + * 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'; +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/CreateSiteHelper.ts b/src/common/chat-participants/powerpages/commands/create-site/CreateSiteHelper.ts index ddf2092aa..77d04bc60 100644 --- a/src/common/chat-participants/powerpages/commands/create-site/CreateSiteHelper.ts +++ b/src/common/chat-participants/powerpages/commands/create-site/CreateSiteHelper.ts @@ -9,10 +9,32 @@ 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 { 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'; +import { ICreateSiteOptions, IPreviewSitePagesContentOptions } from './CreateSiteTypes'; -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 (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({sitePages, stream, extensionContext, telemetry, sessionId, orgId, envId, userId}); + + // TODO: Implement the create site button click handler return { siteName, @@ -46,3 +68,53 @@ async function fetchSiteAndPageData(intelligenceEndpoint: string, intelligenceAp return { siteName, sitePagesList, sitePages, siteDescription }; } + + +function previewSitePagesContent( + options: IPreviewSitePagesContentOptions +): EditableFileSystemProvider { + const { + sitePages, + stream, + extensionContext, + telemetry, + sessionId, + orgId, + envId, + userId + } = options; + + 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 }); + }); + + const sitePagesFolder: vscode.ChatResponseFileTree[] = []; + const contentProvider = new EditableFileSystemProvider(); + // Register the content provider + extensionContext.subscriptions.push( + vscode.workspace.registerFileSystemProvider(EDITABLE_SCHEME, contentProvider, { isCaseSensitive: true }) + ); + + const baseUri = vscode.Uri.parse(`${EDITABLE_SCHEME}:/`); + + 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)); + }); + + telemetry.sendTelemetryEvent(VSCODE_EXTENSION_PREVIEW_SITE_PAGES, { sessionId, orgId, environmentId: envId, userId }); + + 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; + } +} + 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 000000000..30d8f358d --- /dev/null +++ b/src/common/chat-participants/powerpages/commands/create-site/CreateSiteTypes.ts @@ -0,0 +1,33 @@ +/* + * 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; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + sitePages: any[]; + stream: vscode.ChatResponseStream; + extensionContext: vscode.ExtensionContext; + telemetry: ITelemetry; + sessionId: string; + orgId: string; + envId: string; + userId: string; +} 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 d13716af4..0c9af94d3 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,8 +23,10 @@ export async function getNL2SiteData(aibEndpoint: string, aibToken: string, user // "shouldCheckBlockList": false, //TODO: Check if this is needed "version": "V1", "information": { - "minPages": 7, - "maxPages": 7 + "minPages": MIN_PAGES, + "maxPages": MAX_PAGES, + "language": ENGLISH + } } }; diff --git a/src/common/constants.ts b/src/common/constants.ts index e99842934..dfb5f8fba 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'; diff --git a/src/common/utilities/EditableFileSystemProvider.ts b/src/common/utilities/EditableFileSystemProvider.ts new file mode 100644 index 000000000..1d486527e --- /dev/null +++ b/src/common/utilities/EditableFileSystemProvider.ts @@ -0,0 +1,65 @@ +/* + * 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 { + // Copy is not supported in this implementation + } + + // 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') : ''; + } +}