From 89375c4a69a6290029fd036e9e0be7e4ee07cb89 Mon Sep 17 00:00:00 2001 From: Vikash Agrawal Date: Wed, 6 Nov 2024 11:50:41 -0500 Subject: [PATCH] refactor(amazonq): add BaseConnector Abstract class for common logic (#5931) Create an abstract class that can be used by all connectors. --- License: I confirm that my contribution is made under the terms of the Apache 2.0 license. --- .../amazonq/webview/ui/apps/baseConnector.ts | 298 ++++++++++++++++++ .../webview/ui/apps/cwChatConnector.ts | 285 +---------------- .../ui/apps/featureDevChatConnector.ts | 181 +---------- .../webview/ui/apps/gumbyChatConnector.ts | 85 +---- .../core/src/amazonq/webview/ui/connector.ts | 30 +- 5 files changed, 368 insertions(+), 511 deletions(-) create mode 100644 packages/core/src/amazonq/webview/ui/apps/baseConnector.ts diff --git a/packages/core/src/amazonq/webview/ui/apps/baseConnector.ts b/packages/core/src/amazonq/webview/ui/apps/baseConnector.ts new file mode 100644 index 00000000000..e28c599d910 --- /dev/null +++ b/packages/core/src/amazonq/webview/ui/apps/baseConnector.ts @@ -0,0 +1,298 @@ +/*! + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0 + */ + +import { ChatItem, ChatItemAction, ChatItemType, FeedbackPayload } from '@aws/mynah-ui' +import { ExtensionMessage } from '../commands' +import { CodeReference } from './amazonqCommonsConnector' +import { TabOpenType, TabsStorage, TabType } from '../storages/tabsStorage' +import { FollowUpGenerator } from '../followUps/generator' +import { CWCChatItem } from '../connector' + +interface ChatPayload { + chatMessage: string + chatCommand?: string +} + +export interface BaseConnectorProps { + sendMessageToExtension: (message: ExtensionMessage) => void + onMessageReceived?: (tabID: string, messageData: any, needToShowAPIDocsTab: boolean) => void + onChatAnswerReceived?: (tabID: string, message: CWCChatItem | ChatItem, messageData: any) => void + onError: (tabID: string, message: string, title: string) => void + onWarning: (tabID: string, message: string, title: string) => void + onOpenSettingsMessage: (tabID: string) => void + tabsStorage: TabsStorage +} + +export abstract class BaseConnector { + protected readonly sendMessageToExtension + protected readonly onError + protected readonly onWarning + protected readonly onChatAnswerReceived + protected readonly onOpenSettingsMessage + protected readonly followUpGenerator: FollowUpGenerator + protected readonly tabsStorage + + abstract getTabType(): TabType + + constructor(props: BaseConnectorProps) { + this.sendMessageToExtension = props.sendMessageToExtension + this.onChatAnswerReceived = props.onChatAnswerReceived + this.onWarning = props.onWarning + this.onError = props.onError + this.onOpenSettingsMessage = props.onOpenSettingsMessage + this.tabsStorage = props.tabsStorage + this.followUpGenerator = new FollowUpGenerator() + } + + onResponseBodyLinkClick = (tabID: string, messageId: string, link: string): void => { + this.sendMessageToExtension({ + command: 'response-body-link-click', + tabID, + messageId, + link, + tabType: this.getTabType(), + }) + } + onInfoLinkClick = (tabID: string, link: string): void => { + this.sendMessageToExtension({ + command: 'footer-info-link-click', + tabID, + link, + tabType: this.getTabType(), + }) + } + + followUpClicked = (tabID: string, messageId: string, followUp: ChatItemAction): void => { + /** + * We've pressed on a followup button and should start watching that round trip telemetry + */ + this.sendMessageToExtension({ + command: 'start-chat-message-telemetry', + trigger: 'followUpClicked', + tabID, + traceId: messageId, + tabType: this.getTabType(), + startTime: Date.now(), + }) + this.sendMessageToExtension({ + command: 'follow-up-was-clicked', + followUp, + tabID, + messageId, + tabType: this.getTabType(), + }) + } + + onTabAdd = (tabID: string, tabOpenInteractionType?: TabOpenType): void => { + this.sendMessageToExtension({ + tabID: tabID, + command: 'new-tab-was-created', + tabType: this.getTabType(), + tabOpenInteractionType, + }) + } + + onCodeInsertToCursorPosition = ( + tabID: string, + messageId: string, + code?: string, + type?: 'selection' | 'block', + codeReference?: CodeReference[], + eventId?: string, + codeBlockIndex?: number, + totalCodeBlocks?: number, + userIntent?: string, + codeBlockLanguage?: string + ): void => { + this.sendMessageToExtension({ + tabID: tabID, + messageId, + code, + command: 'insert_code_at_cursor_position', + tabType: this.getTabType(), + insertionTargetType: type, + codeReference, + eventId, + codeBlockIndex, + totalCodeBlocks, + userIntent, + codeBlockLanguage, + }) + } + + onCopyCodeToClipboard = ( + tabID: string, + messageId: string, + code?: string, + type?: 'selection' | 'block', + codeReference?: CodeReference[], + eventId?: string, + codeBlockIndex?: number, + totalCodeBlocks?: number, + userIntent?: string, + codeBlockLanguage?: string + ): void => { + this.sendMessageToExtension({ + tabID: tabID, + messageId, + code, + command: 'code_was_copied_to_clipboard', + tabType: this.getTabType(), + insertionTargetType: type, + codeReference, + eventId, + codeBlockIndex, + totalCodeBlocks, + userIntent, + codeBlockLanguage, + }) + } + + onTabRemove = (tabID: string): void => { + this.sendMessageToExtension({ + tabID: tabID, + command: 'tab-was-removed', + tabType: this.getTabType(), + }) + } + + onTabChange = (tabID: string, prevTabID?: string) => { + this.sendMessageToExtension({ + tabID: tabID, + command: 'tab-was-changed', + tabType: this.getTabType(), + prevTabID, + }) + } + + onStopChatResponse = (tabID: string): void => { + this.sendMessageToExtension({ + tabID: tabID, + command: 'stop-response', + tabType: this.getTabType(), + }) + } + + onChatItemVoted = (tabID: string, messageId: string, vote: 'upvote' | 'downvote'): void => { + this.sendMessageToExtension({ + tabID: tabID, + command: 'chat-item-voted', + messageId, + vote, + tabType: this.getTabType(), + }) + } + onSendFeedback = (tabID: string, feedbackPayload: FeedbackPayload): void | undefined => { + this.sendMessageToExtension({ + command: 'chat-item-feedback', + ...feedbackPayload, + tabType: this.getTabType(), + tabID: tabID, + }) + } + + requestGenerativeAIAnswer = (tabID: string, messageId: string, payload: ChatPayload): Promise => { + /** + * When a user presses "enter" send an event that indicates + * we should start tracking the round trip time for this message + **/ + this.sendMessageToExtension({ + command: 'start-chat-message-telemetry', + trigger: 'onChatPrompt', + tabID, + traceId: messageId, + tabType: this.getTabType(), + startTime: Date.now(), + }) + return new Promise((resolve, reject) => { + this.sendMessageToExtension({ + tabID: tabID, + command: 'chat-prompt', + chatMessage: payload.chatMessage, + chatCommand: payload.chatCommand, + tabType: this.getTabType(), + }) + }) + } + + clearChat = (tabID: string): void => { + this.sendMessageToExtension({ + tabID: tabID, + command: 'clear', + chatMessage: '', + tabType: this.getTabType(), + }) + } + + help = (tabID: string): void => { + this.sendMessageToExtension({ + tabID: tabID, + command: 'help', + chatMessage: '', + tabType: this.getTabType(), + }) + } + + onTabOpen = (tabID: string): void => { + this.sendMessageToExtension({ + tabID, + command: 'new-tab-was-created', + tabType: this.getTabType(), + }) + } + + protected sendTriggerMessageProcessed = async (requestID: any): Promise => { + this.sendMessageToExtension({ + command: 'trigger-message-processed', + requestID: requestID, + tabType: this.getTabType(), + }) + } + + protected processAuthNeededException = async (messageData: any): Promise => { + if (this.onChatAnswerReceived === undefined) { + return + } + + this.onChatAnswerReceived( + messageData.tabID, + { + type: ChatItemType.ANSWER, + messageId: messageData.triggerID, + body: messageData.message, + followUp: this.followUpGenerator.generateAuthFollowUps(this.getTabType(), messageData.authType), + canBeVoted: false, + }, + messageData + ) + + return + } + + protected processOpenSettingsMessage = async (messageData: any): Promise => { + this.onOpenSettingsMessage(messageData.tabID) + } + + protected baseHandleMessageReceive = async (messageData: any): Promise => { + if (messageData.type === 'errorMessage') { + this.onError(messageData.tabID, messageData.message, messageData.title) + return + } + if (messageData.type === 'showInvalidTokenNotification') { + this.onWarning(messageData.tabID, messageData.message, messageData.title) + return + } + + if (messageData.type === 'authNeededException') { + await this.processAuthNeededException(messageData) + return + } + + if (messageData.type === 'openSettingsMessage') { + await this.processOpenSettingsMessage(messageData) + return + } + } +} diff --git a/packages/core/src/amazonq/webview/ui/apps/cwChatConnector.ts b/packages/core/src/amazonq/webview/ui/apps/cwChatConnector.ts index 3e5ea73b3ba..58f525857a8 100644 --- a/packages/core/src/amazonq/webview/ui/apps/cwChatConnector.ts +++ b/packages/core/src/amazonq/webview/ui/apps/cwChatConnector.ts @@ -3,46 +3,25 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { ChatItemAction, ChatItemType, FeedbackPayload } from '@aws/mynah-ui' -import { ExtensionMessage } from '../commands' -import { CodeReference } from './amazonqCommonsConnector' -import { TabOpenType, TabsStorage } from '../storages/tabsStorage' -import { FollowUpGenerator } from '../followUps/generator' +import { ChatItemType } from '@aws/mynah-ui' +import { TabType } from '../storages/tabsStorage' import { CWCChatItem } from '../connector' +import { BaseConnector, BaseConnectorProps } from './baseConnector' -interface ChatPayload { - chatMessage: string - chatCommand?: string -} - -export interface ConnectorProps { - sendMessageToExtension: (message: ExtensionMessage) => void - onMessageReceived?: (tabID: string, messageData: any, needToShowAPIDocsTab: boolean) => void - onChatAnswerReceived?: (tabID: string, message: CWCChatItem, messageData: any) => void +export interface ConnectorProps extends BaseConnectorProps { onCWCContextCommandMessage: (message: CWCChatItem, command?: string) => string | undefined - onError: (tabID: string, message: string, title: string) => void - onWarning: (tabID: string, message: string, title: string) => void - onOpenSettingsMessage: (tabID: string) => void - tabsStorage: TabsStorage } -export class Connector { - private readonly sendMessageToExtension - private readonly onError - private readonly onWarning - private readonly onChatAnswerReceived +export class Connector extends BaseConnector { private readonly onCWCContextCommandMessage - private readonly onOpenSettingsMessage - private readonly followUpGenerator: FollowUpGenerator + + override getTabType(): TabType { + return 'cwc' + } constructor(props: ConnectorProps) { - this.sendMessageToExtension = props.sendMessageToExtension - this.onChatAnswerReceived = props.onChatAnswerReceived - this.onWarning = props.onWarning - this.onError = props.onError + super(props) this.onCWCContextCommandMessage = props.onCWCContextCommandMessage - this.onOpenSettingsMessage = props.onOpenSettingsMessage - this.followUpGenerator = new FollowUpGenerator() } onSourceLinkClick = (tabID: string, messageId: string, link: string): void => { @@ -51,203 +30,7 @@ export class Connector { tabID, messageId, link, - tabType: 'cwc', - }) - } - onResponseBodyLinkClick = (tabID: string, messageId: string, link: string): void => { - this.sendMessageToExtension({ - command: 'response-body-link-click', - tabID, - messageId, - link, - tabType: 'cwc', - }) - } - onInfoLinkClick = (tabID: string, link: string): void => { - this.sendMessageToExtension({ - command: 'footer-info-link-click', - tabID, - link, - tabType: 'cwc', - }) - } - - followUpClicked = (tabID: string, messageId: string, followUp: ChatItemAction): void => { - /** - * We've pressed on a followup button and should start watching that round trip telemetry - */ - this.sendMessageToExtension({ - command: 'start-chat-message-telemetry', - trigger: 'followUpClicked', - tabID, - traceId: messageId, - tabType: 'cwc', - startTime: Date.now(), - }) - this.sendMessageToExtension({ - command: 'follow-up-was-clicked', - followUp, - tabID, - messageId, - tabType: 'cwc', - }) - } - - onTabAdd = (tabID: string, tabOpenInteractionType?: TabOpenType): void => { - this.sendMessageToExtension({ - tabID: tabID, - command: 'new-tab-was-created', - tabType: 'cwc', - tabOpenInteractionType, - }) - } - - onCodeInsertToCursorPosition = ( - tabID: string, - messageId: string, - code?: string, - type?: 'selection' | 'block', - codeReference?: CodeReference[], - eventId?: string, - codeBlockIndex?: number, - totalCodeBlocks?: number, - userIntent?: string, - codeBlockLanguage?: string - ): void => { - this.sendMessageToExtension({ - tabID: tabID, - messageId, - code, - command: 'insert_code_at_cursor_position', - tabType: 'cwc', - insertionTargetType: type, - codeReference, - eventId, - codeBlockIndex, - totalCodeBlocks, - userIntent, - codeBlockLanguage, - }) - } - - onCopyCodeToClipboard = ( - tabID: string, - messageId: string, - code?: string, - type?: 'selection' | 'block', - codeReference?: CodeReference[], - eventId?: string, - codeBlockIndex?: number, - totalCodeBlocks?: number, - userIntent?: string, - codeBlockLanguage?: string - ): void => { - this.sendMessageToExtension({ - tabID: tabID, - messageId, - code, - command: 'code_was_copied_to_clipboard', - tabType: 'cwc', - insertionTargetType: type, - codeReference, - eventId, - codeBlockIndex, - totalCodeBlocks, - userIntent, - codeBlockLanguage, - }) - } - - onTabRemove = (tabID: string): void => { - this.sendMessageToExtension({ - tabID: tabID, - command: 'tab-was-removed', - tabType: 'cwc', - }) - } - - onTabChange = (tabID: string, prevTabID?: string) => { - this.sendMessageToExtension({ - tabID: tabID, - command: 'tab-was-changed', - tabType: 'cwc', - prevTabID, - }) - } - - onStopChatResponse = (tabID: string): void => { - this.sendMessageToExtension({ - tabID: tabID, - command: 'stop-response', - tabType: 'cwc', - }) - } - - onChatItemVoted = (tabID: string, messageId: string, vote: 'upvote' | 'downvote'): void => { - this.sendMessageToExtension({ - tabID: tabID, - command: 'chat-item-voted', - messageId, - vote, - tabType: 'cwc', - }) - } - onSendFeedback = (tabID: string, feedbackPayload: FeedbackPayload): void | undefined => { - this.sendMessageToExtension({ - command: 'chat-item-feedback', - ...feedbackPayload, - tabType: 'cwc', - tabID: tabID, - }) - } - - requestGenerativeAIAnswer = (tabID: string, messageId: string, payload: ChatPayload): Promise => { - /** - * When a user presses "enter" send an event that indicates - * we should start tracking the round trip time for this message - **/ - this.sendMessageToExtension({ - command: 'start-chat-message-telemetry', - trigger: 'onChatPrompt', - tabID, - traceId: messageId, - tabType: 'cwc', - startTime: Date.now(), - }) - return new Promise((resolve, reject) => { - this.sendMessageToExtension({ - tabID: tabID, - command: 'chat-prompt', - chatMessage: payload.chatMessage, - chatCommand: payload.chatCommand, - tabType: 'cwc', - }) - }) - } - - clearChat = (tabID: string): void => { - this.sendMessageToExtension({ - tabID: tabID, - command: 'clear', - chatMessage: '', - tabType: 'cwc', - }) - } - - help = (tabID: string): void => { - this.sendMessageToExtension({ - tabID: tabID, - command: 'help', - chatMessage: '', - tabType: 'cwc', - }) - } - - private sendTriggerMessageProcessed = async (requestID: any): Promise => { - this.sendMessageToExtension({ - command: 'trigger-message-processed', - requestID: requestID, - tabType: 'cwc', + tabType: this.getTabType(), }) } @@ -270,7 +53,7 @@ export class Connector { command: 'trigger-tabID-received', triggerID, tabID, - tabType: 'cwc', + tabType: this.getTabType(), }) } @@ -348,40 +131,7 @@ export class Connector { } } - private processAuthNeededException = async (messageData: any): Promise => { - if (this.onChatAnswerReceived === undefined) { - return - } - - this.onChatAnswerReceived( - messageData.tabID, - { - type: ChatItemType.ANSWER, - messageId: messageData.triggerID, - body: messageData.message, - followUp: this.followUpGenerator.generateAuthFollowUps('cwc', messageData.authType), - canBeVoted: false, - }, - messageData - ) - - return - } - - private processOpenSettingsMessage = async (messageData: any): Promise => { - this.onOpenSettingsMessage(messageData.tabID) - } - handleMessageReceive = async (messageData: any): Promise => { - if (messageData.type === 'errorMessage') { - this.onError(messageData.tabID, messageData.message, messageData.title) - return - } - if (messageData.type === 'showInvalidTokenNotification') { - this.onWarning(messageData.tabID, messageData.message, messageData.title) - return - } - if (messageData.type === 'chatMessage') { await this.processChatMessage(messageData) return @@ -392,14 +142,7 @@ export class Connector { return } - if (messageData.type === 'authNeededException') { - await this.processAuthNeededException(messageData) - return - } - - if (messageData.type === 'openSettingsMessage') { - await this.processOpenSettingsMessage(messageData) - return - } + // For other message types, call the base class handleMessageReceive + await this.baseHandleMessageReceive(messageData) } } diff --git a/packages/core/src/amazonq/webview/ui/apps/featureDevChatConnector.ts b/packages/core/src/amazonq/webview/ui/apps/featureDevChatConnector.ts index cca7ee8dee8..ddc6c68d99c 100644 --- a/packages/core/src/amazonq/webview/ui/apps/featureDevChatConnector.ts +++ b/packages/core/src/amazonq/webview/ui/apps/featureDevChatConnector.ts @@ -3,21 +3,13 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { ChatItem, ChatItemAction, ChatItemType, FeedbackPayload } from '@aws/mynah-ui' -import { ExtensionMessage } from '../commands' -import { TabType, TabsStorage } from '../storages/tabsStorage' -import { CodeReference } from './amazonqCommonsConnector' -import { FollowUpGenerator } from '../followUps/generator' +import { ChatItem, ChatItemType, FeedbackPayload } from '@aws/mynah-ui' +import { TabType } from '../storages/tabsStorage' import { getActions } from '../diffTree/actions' import { DiffTreeFileInfo } from '../diffTree/types' +import { BaseConnector, BaseConnectorProps } from './baseConnector' -interface ChatPayload { - chatMessage: string -} - -export interface ConnectorProps { - sendMessageToExtension: (message: ExtensionMessage) => void - onMessageReceived?: (tabID: string, messageData: any, needToShowAPIDocsTab: boolean) => void +export interface ConnectorProps extends BaseConnectorProps { onAsyncEventProgress: ( tabID: string, inProgress: boolean, @@ -25,10 +17,7 @@ export interface ConnectorProps { messageId: string | undefined, enableStopAction: boolean ) => void - onChatAnswerReceived?: (tabID: string, message: ChatItem, messageData: any) => void sendFeedback?: (tabId: string, feedbackPayload: FeedbackPayload) => void | undefined - onError: (tabID: string, message: string, title: string) => void - onWarning: (tabID: string, message: string, title: string) => void onFileComponentUpdate: ( tabID: string, filePaths: DiffTreeFileInfo[], @@ -40,66 +29,30 @@ export interface ConnectorProps { onChatInputEnabled: (tabID: string, enabled: boolean) => void onUpdateAuthentication: (featureDevEnabled: boolean, authenticatingTabIDs: string[]) => void onNewTab: (tabType: TabType) => void - tabsStorage: TabsStorage } -export class Connector { - private readonly sendMessageToExtension - private readonly onError - private readonly onWarning +export class Connector extends BaseConnector { private readonly onFileComponentUpdate - private readonly onChatAnswerReceived private readonly onAsyncEventProgress private readonly updatePlaceholder private readonly chatInputEnabled private readonly onUpdateAuthentication - private readonly followUpGenerator: FollowUpGenerator private readonly onNewTab + override getTabType(): TabType { + return 'featuredev' + } + constructor(props: ConnectorProps) { - this.sendMessageToExtension = props.sendMessageToExtension - this.onChatAnswerReceived = props.onChatAnswerReceived - this.onWarning = props.onWarning + super(props) this.onFileComponentUpdate = props.onFileComponentUpdate - this.onError = props.onError this.onAsyncEventProgress = props.onAsyncEventProgress this.updatePlaceholder = props.onUpdatePlaceholder this.chatInputEnabled = props.onChatInputEnabled this.onUpdateAuthentication = props.onUpdateAuthentication - this.followUpGenerator = new FollowUpGenerator() this.onNewTab = props.onNewTab } - onCodeInsertToCursorPosition = ( - tabID: string, - code?: string, - type?: 'selection' | 'block', - codeReference?: CodeReference[] - ): void => { - this.sendMessageToExtension({ - tabID: tabID, - code, - command: 'insert_code_at_cursor_position', - codeReference, - tabType: 'featuredev', - }) - } - - onCopyCodeToClipboard = ( - tabID: string, - code?: string, - type?: 'selection' | 'block', - codeReference?: CodeReference[] - ): void => { - this.sendMessageToExtension({ - tabID: tabID, - code, - command: 'code_was_copied_to_clipboard', - codeReference, - tabType: 'featuredev', - }) - } - onOpenDiff = (tabID: string, filePath: string, deleted: boolean, messageId?: string): void => { this.sendMessageToExtension({ command: 'open-diff', @@ -107,7 +60,7 @@ export class Connector { filePath, deleted, messageId, - tabType: 'featuredev', + tabType: this.getTabType(), }) } onFileActionClick = (tabID: string, messageId: string, filePath: string, actionName: string): void => { @@ -117,29 +70,10 @@ export class Connector { messageId, filePath, actionName, - tabType: 'featuredev', + tabType: this.getTabType(), }) } - followUpClicked = (tabID: string, followUp: ChatItemAction): void => { - this.sendMessageToExtension({ - command: 'follow-up-was-clicked', - followUp, - tabID, - tabType: 'featuredev', - }) - } - - requestGenerativeAIAnswer = (tabID: string, payload: ChatPayload): Promise => - new Promise((resolve, reject) => { - this.sendMessageToExtension({ - tabID: tabID, - command: 'chat-prompt', - chatMessage: payload.chatMessage, - tabType: 'featuredev', - }) - }) - private processChatMessage = async (messageData: any): Promise => { if (this.onChatAnswerReceived !== undefined) { const answer: ChatItem = { @@ -191,36 +125,6 @@ export class Connector { } } - private processAuthNeededException = async (messageData: any): Promise => { - if (this.onChatAnswerReceived === undefined) { - return - } - - this.onChatAnswerReceived( - messageData.tabID, - { - type: ChatItemType.ANSWER, - body: messageData.message, - followUp: undefined, - canBeVoted: false, - }, - messageData - ) - - this.onChatAnswerReceived( - messageData.tabID, - { - type: ChatItemType.SYSTEM_PROMPT, - body: undefined, - followUp: this.followUpGenerator.generateAuthFollowUps('featuredev', messageData.authType), - canBeVoted: false, - }, - messageData - ) - - return - } - handleMessageReceive = async (messageData: any): Promise => { if (messageData.type === 'updateFileComponent') { this.onFileComponentUpdate( @@ -231,15 +135,6 @@ export class Connector { ) return } - if (messageData.type === 'errorMessage') { - this.onError(messageData.tabID, messageData.message, messageData.title) - return - } - - if (messageData.type === 'showInvalidTokenNotification') { - this.onWarning(messageData.tabID, messageData.message, messageData.title) - return - } if (messageData.type === 'chatMessage') { await this.processChatMessage(messageData) @@ -278,67 +173,21 @@ export class Connector { return } - if (messageData.type === 'authNeededException') { - await this.processAuthNeededException(messageData) - return - } - if (messageData.type === 'openNewTabMessage') { this.onNewTab('featuredev') return } - } - - onStopChatResponse = (tabID: string): void => { - this.sendMessageToExtension({ - tabID: tabID, - command: 'stop-response', - tabType: 'featuredev', - }) - } - - onTabOpen = (tabID: string): void => { - this.sendMessageToExtension({ - tabID, - command: 'new-tab-was-created', - tabType: 'featuredev', - }) - } - onTabRemove = (tabID: string): void => { - this.sendMessageToExtension({ - tabID: tabID, - command: 'tab-was-removed', - tabType: 'featuredev', - }) + // For other message types, call the base class handleMessageReceive + await this.baseHandleMessageReceive(messageData) } sendFeedback = (tabId: string, feedbackPayload: FeedbackPayload): void | undefined => { this.sendMessageToExtension({ command: 'chat-item-feedback', ...feedbackPayload, - tabType: 'featuredev', + tabType: this.getTabType(), tabID: tabId, }) } - - onChatItemVoted = (tabId: string, messageId: string, vote: string): void | undefined => { - this.sendMessageToExtension({ - tabID: tabId, - messageId: messageId, - vote: vote, - command: 'chat-item-voted', - tabType: 'featuredev', - }) - } - - onResponseBodyLinkClick = (tabID: string, messageId: string, link: string): void => { - this.sendMessageToExtension({ - command: 'response-body-link-click', - tabID, - messageId, - link, - tabType: 'featuredev', - }) - } } diff --git a/packages/core/src/amazonq/webview/ui/apps/gumbyChatConnector.ts b/packages/core/src/amazonq/webview/ui/apps/gumbyChatConnector.ts index a6c7d2efef2..9801ff7d27f 100644 --- a/packages/core/src/amazonq/webview/ui/apps/gumbyChatConnector.ts +++ b/packages/core/src/amazonq/webview/ui/apps/gumbyChatConnector.ts @@ -8,14 +8,12 @@ */ import { ChatItem, ChatItemType } from '@aws/mynah-ui' -import { ExtensionMessage } from '../commands' -import { TabOpenType, TabsStorage } from '../storages/tabsStorage' +import { TabType } from '../storages/tabsStorage' import { GumbyMessageType } from '../../../../amazonqGumby/chat/views/connector/connector' import { ChatPayload } from '../connector' +import { BaseConnector, BaseConnectorProps } from './baseConnector' -export interface ConnectorProps { - sendMessageToExtension: (message: ExtensionMessage) => void - onMessageReceived?: (tabID: string, messageData: any, needToShowAPIDocsTab: boolean) => void +export interface ConnectorProps extends BaseConnectorProps { onAsyncEventProgress: ( tabID: string, inProgress: boolean, @@ -23,15 +21,11 @@ export interface ConnectorProps { messageId: string, enableStopAction: boolean ) => void - onChatAnswerReceived?: (tabID: string, message: ChatItem, messageData: any) => void onChatAnswerUpdated?: (tabID: string, message: ChatItem) => void onQuickHandlerCommand: (tabID: string, command: string, eventId?: string) => void - onError: (tabID: string, message: string, title: string) => void - onWarning: (tabID: string, message: string, title: string) => void onUpdateAuthentication: (gumbyEnabled: boolean, authenticatingTabIDs: string[]) => void onChatInputEnabled: (tabID: string, enabled: boolean) => void onUpdatePlaceholder: (tabID: string, newPlaceholder: string) => void - tabsStorage: TabsStorage } export interface MessageData { @@ -39,46 +33,26 @@ export interface MessageData { type: GumbyMessageType } -export class Connector { +export class Connector extends BaseConnector { private readonly onAuthenticationUpdate - private readonly sendMessageToExtension - private readonly onError - private readonly onChatAnswerReceived private readonly onChatAnswerUpdated private readonly chatInputEnabled private readonly onAsyncEventProgress private readonly onQuickHandlerCommand private readonly updatePlaceholder - private readonly tabStorage + + override getTabType(): TabType { + return 'gumby' + } constructor(props: ConnectorProps) { - this.sendMessageToExtension = props.sendMessageToExtension - this.onChatAnswerReceived = props.onChatAnswerReceived + super(props) this.onChatAnswerUpdated = props.onChatAnswerUpdated - this.onError = props.onError this.chatInputEnabled = props.onChatInputEnabled this.onAsyncEventProgress = props.onAsyncEventProgress this.updatePlaceholder = props.onUpdatePlaceholder this.onQuickHandlerCommand = props.onQuickHandlerCommand this.onAuthenticationUpdate = props.onUpdateAuthentication - this.tabStorage = props.tabsStorage - } - - onTabAdd = (tabID: string, tabOpenInteractionType?: TabOpenType): void => { - this.sendMessageToExtension({ - tabID: tabID, - command: 'new-tab-was-created', - tabType: 'gumby', - tabOpenInteractionType, - }) - } - - onTabRemove(tabID: string) { - this.sendMessageToExtension({ - tabID: tabID, - command: 'tab-was-removed', - tabType: 'gumby', - }) } private processChatPrompt = async (messageData: any, tabID: string): Promise => { @@ -129,36 +103,21 @@ export class Connector { tabID: tabID, command: 'transform', chatMessage: 'transform', - tabType: 'gumby', + tabType: this.getTabType(), }) } requestAnswer = (tabID: string, payload: ChatPayload) => { - this.tabStorage.updateTabStatus(tabID, 'busy') + this.tabsStorage.updateTabStatus(tabID, 'busy') this.sendMessageToExtension({ tabID: tabID, command: 'chat-prompt', chatMessage: payload.chatMessage, chatCommand: payload.chatCommand, - tabType: 'gumby', + tabType: this.getTabType(), }) } - private processAuthNeededException = async (messageData: any): Promise => { - if (this.onChatAnswerReceived === undefined) { - return - } - - this.onChatAnswerReceived( - messageData.tabID, - { - type: ChatItemType.SYSTEM_PROMPT, - body: messageData.message, - }, - messageData - ) - } - onCustomFormAction( tabId: string, action: { @@ -175,21 +134,11 @@ export class Connector { command: 'form-action-click', action: action.id, formSelectedValues: action.formItemValues, - tabType: 'gumby', + tabType: this.getTabType(), tabID: tabId, }) } - onResponseBodyLinkClick = (tabID: string, messageId: string, link: string): void => { - this.sendMessageToExtension({ - command: 'response-body-link-click', - tabID, - messageId, - link, - tabType: 'gumby', - }) - } - private processExecuteCommand = async (messageData: any): Promise => { this.onQuickHandlerCommand(messageData.tabID, messageData.command, messageData.eventId) } @@ -206,9 +155,6 @@ export class Connector { false ) break - case 'authNeededException': - await this.processAuthNeededException(messageData) - break case 'authenticationUpdateMessage': this.onAuthenticationUpdate(messageData.gumbyEnabled, messageData.authenticatingTabIDs) break @@ -221,15 +167,14 @@ export class Connector { case 'chatPrompt': await this.processChatPrompt(messageData, messageData.tabID) break - case 'errorMessage': - this.onError(messageData.tabID, messageData.message, messageData.title) - break case 'sendCommandMessage': await this.processExecuteCommand(messageData) break case 'updatePlaceholderMessage': this.updatePlaceholder(messageData.tabID, messageData.newPlaceholder) break + default: + await this.baseHandleMessageReceive(messageData) } } } diff --git a/packages/core/src/amazonq/webview/ui/connector.ts b/packages/core/src/amazonq/webview/ui/connector.ts index 1d832c8e023..376ca1ca42c 100644 --- a/packages/core/src/amazonq/webview/ui/connector.ts +++ b/packages/core/src/amazonq/webview/ui/connector.ts @@ -145,7 +145,7 @@ export class Connector { if (this.isUIReady) { switch (this.tabsStorage.getTab(tabID)?.type) { case 'featuredev': - return this.featureDevChatConnector.requestGenerativeAIAnswer(tabID, payload) + return this.featureDevChatConnector.requestGenerativeAIAnswer(tabID, messageId, payload) default: return this.cwChatConnector.requestGenerativeAIAnswer(tabID, messageId, payload) } @@ -273,7 +273,18 @@ export class Connector { ) break case 'featuredev': - this.featureDevChatConnector.onCodeInsertToCursorPosition(tabID, code, type, codeReference) + this.featureDevChatConnector.onCodeInsertToCursorPosition( + tabID, + messageId, + code, + type, + codeReference, + eventId, + codeBlockIndex, + totalCodeBlocks, + userIntent, + codeBlockLanguage + ) break } } @@ -364,7 +375,18 @@ export class Connector { ) break case 'featuredev': - this.featureDevChatConnector.onCopyCodeToClipboard(tabID, code, type, codeReference) + this.featureDevChatConnector.onCopyCodeToClipboard( + tabID, + messageId, + code, + type, + codeReference, + eventId, + codeBlockIndex, + totalCodeBlocks, + userIntent, + codeBlockLanguage + ) break } } @@ -444,7 +466,7 @@ export class Connector { this.amazonqCommonsConnector.followUpClicked(tabID, followUp) break case 'featuredev': - this.featureDevChatConnector.followUpClicked(tabID, followUp) + this.featureDevChatConnector.followUpClicked(tabID, messageId, followUp) break default: this.cwChatConnector.followUpClicked(tabID, messageId, followUp)