Skip to content

Commit 2bf2431

Browse files
committed
viewDiffInChat
1 parent 7b296f2 commit 2bf2431

File tree

6 files changed

+113
-44
lines changed

6 files changed

+113
-44
lines changed

plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/webview/Browser.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ class Browser(parent: Disposable, private val webUri: URI, val project: Project)
139139
$postMessageToJavaJsCode
140140
}
141141
},
142-
142+
${CodeWhispererFeatureConfigService.getInstance().getFeatureConfigJsonString()},
143143
"${activeProfile?.profileName.orEmpty()}")
144144
const commands = [hybridChatConnector.initialQuickActions[0], hybridChatConnector.initialQuickActions[1]]
145145
amazonQChat.createChat(

plugins/amazonq/mynah-ui/src/mynah-ui/connectorAdapter.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { WebviewUIHandler } from './ui/main'
88
import { TabDataGenerator } from './ui/tabs/generator'
99
import { ChatClientAdapter, ChatEventHandler } from '@aws/chat-client'
1010
import { FqnExtractor } from "./fqn/extractor";
11+
import {FeatureContext} from "./ui/types";
1112

1213
export * from "./ui/main";
1314

@@ -25,8 +26,9 @@ export const initiateAdapter = (showWelcomePage: boolean,
2526
isCodeScanEnabled: boolean,
2627
isCodeTestEnabled: boolean,
2728
ideApiPostMessage: (message: any) => void,
28-
profileName?: string) : HybridChatAdapter => {
29-
return new HybridChatAdapter(showWelcomePage, disclaimerAcknowledged, isFeatureDevEnabled, isCodeTransformEnabled, isDocEnabled, isCodeScanEnabled, isCodeTestEnabled, ideApiPostMessage, profileName)
29+
featureConfigsSerialized: [string, FeatureContext][],
30+
profileName?: string,) : HybridChatAdapter => {
31+
return new HybridChatAdapter(showWelcomePage, disclaimerAcknowledged, isFeatureDevEnabled, isCodeTransformEnabled, isDocEnabled, isCodeScanEnabled, isCodeTestEnabled, ideApiPostMessage, featureConfigsSerialized, profileName)
3032
}
3133

3234

@@ -37,7 +39,6 @@ export class HybridChatAdapter implements ChatClientAdapter {
3739
private mynahUIRef?: { mynahUI: MynahUI}
3840

3941
constructor(
40-
4142
private showWelcomePage: boolean,
4243
private disclaimerAcknowledged: boolean,
4344
private isFeatureDevEnabled: boolean,
@@ -46,8 +47,8 @@ export class HybridChatAdapter implements ChatClientAdapter {
4647
private isCodeScanEnabled: boolean,
4748
private isCodeTestEnabled: boolean,
4849
private ideApiPostMessage: (message: any) => void,
50+
private featureConfigsSerialized: [string, FeatureContext][],
4951
private profileName?: string,
50-
5152
) {}
5253

5354
/**
@@ -69,6 +70,7 @@ export class HybridChatAdapter implements ChatClientAdapter {
6970
isCodeTestEnabled: this.isCodeTestEnabled,
7071
profileName: this.profileName,
7172
hybridChat: true,
73+
featureConfigsSerialized: this.featureConfigsSerialized,
7274
})
7375

7476
return this.uiHandler.mynahUIProps

plugins/amazonq/mynah-ui/src/mynah-ui/ui/connector.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ export interface CWCChatItem extends ChatItem {
4949
export interface ConnectorProps {
5050
sendMessageToExtension: (message: ExtensionMessage) => void
5151
onMessageReceived?: (tabID: string, messageData: any, needToShowAPIDocsTab: boolean) => void
52-
onChatAnswerReceived?: (tabID: string, message: ChatItem) => void
52+
onChatAnswerReceived?: (tabID: string, message: ChatItem, messageData?: any) => void
5353
onChatAnswerUpdated?: (tabID: string, message:ChatItem) => void
5454
onCodeTransformChatDisabled: (tabID: string) => void
5555
onCodeTransformMessageReceived: (
@@ -62,6 +62,7 @@ export interface ConnectorProps {
6262
onRunTestMessageReceived?: (tabID: string, showRunTestMessage: boolean) => void
6363
onWelcomeFollowUpClicked: (tabID: string, welcomeFollowUpType: WelcomeFollowupType) => void
6464
onAsyncEventProgress: (tabID: string, inProgress: boolean, message: string | undefined, cancelButtonWhenLoading?: boolean) => void
65+
onQuickHandlerCommand: (tabID: string, command?: string, eventId?: string) => void
6566
onCWCContextCommandMessage: (message: ChatItem, command?: string) => string | undefined
6667
onCWCOnboardingPageInteractionMessage: (message: ChatItem) => string | undefined
6768
onOpenSettingsMessage: (tabID: string) => void
@@ -281,6 +282,8 @@ export class Connector {
281282
default:
282283
break
283284
}
285+
// Reset lastCommand after message is rendered.
286+
this.tabsStorage.updateTabLastCommand(messageData.tabID, '')
284287
}
285288

286289
onTabAdd = (tabID: string): void => {

plugins/amazonq/mynah-ui/src/mynah-ui/ui/main.ts

Lines changed: 63 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -31,42 +31,11 @@ import {welcomeScreenTabData} from "./walkthrough/welcome";
3131
import { agentWalkthroughDataModel } from './walkthrough/agent'
3232
import {createClickTelemetry, createOpenAgentTelemetry} from "./telemetry/actions";
3333
import {disclaimerAcknowledgeButtonId, disclaimerCard} from "./texts/disclaimer";
34+
import {FeatureContext, tryNewMap} from "./types";
35+
3436

3537

3638
// Ref: https://github.com/aws/aws-toolkit-vscode/blob/e9ea8082ffe0b9968a873437407d0b6b31b9e1a5/packages/core/src/amazonq/webview/ui/main.ts
37-
export const createMynahUI = (
38-
ideApi: any,
39-
showWelcomePage: boolean,
40-
disclaimerAcknowledged: boolean,
41-
isFeatureDevEnabled: boolean,
42-
isCodeTransformEnabled: boolean,
43-
isDocEnabled: boolean,
44-
isCodeScanEnabled: boolean,
45-
isCodeTestEnabled: boolean,
46-
highlightCommand?: QuickActionCommand,
47-
profileName?: string,
48-
49-
) => {
50-
const handler = new WebviewUIHandler({
51-
postMessage: ideApi.postMessage,
52-
mynahUIRef: { mynahUI: undefined },
53-
showWelcomePage,
54-
disclaimerAcknowledged,
55-
isFeatureDevEnabled,
56-
isCodeTransformEnabled,
57-
isDocEnabled,
58-
isCodeScanEnabled,
59-
isCodeTestEnabled,
60-
highlightCommand,
61-
profileName,
62-
hybridChat: false,
63-
})
64-
65-
return {
66-
mynahUI: handler.mynahUI,
67-
messageReceiver: handler.connector?.handleMessageReceive,
68-
}
69-
}
7039

7140
export class WebviewUIHandler {
7241
postMessage: any
@@ -81,6 +50,7 @@ export class WebviewUIHandler {
8150
profileName?: string
8251
responseMetadata: Map<string, string[]>
8352
tabsStorage: TabsStorage
53+
featureConfigs?: Map<string, FeatureContext>
8454

8555
mynahUIProps: MynahUIProps
8656
connector?: Connector
@@ -90,14 +60,14 @@ export class WebviewUIHandler {
9060
textMessageHandler?: TextMessageHandler
9161
messageController?: MessageController
9262

93-
savedContextCommands: MynahUIDataModel['contextCommands']
9463
disclaimerCardActive : boolean
9564

9665

9766
mynahUIRef: { mynahUI: MynahUI | undefined }
9867
constructor({
9968
postMessage,
10069
mynahUIRef,
70+
featureConfigsSerialized,
10171
showWelcomePage,
10272
disclaimerAcknowledged,
10373
isFeatureDevEnabled,
@@ -112,6 +82,7 @@ export class WebviewUIHandler {
11282
} : {
11383
postMessage: any
11484
mynahUIRef: { mynahUI: MynahUI | undefined }
85+
featureConfigsSerialized: [string, FeatureContext][]
11586
showWelcomePage: boolean,
11687
disclaimerAcknowledged: boolean,
11788
isFeatureDevEnabled: boolean
@@ -127,6 +98,7 @@ export class WebviewUIHandler {
12798
}) {
12899
this.postMessage = postMessage
129100
this.mynahUIRef = mynahUIRef
101+
this.featureConfigs = tryNewMap(featureConfigsSerialized)
130102
this.showWelcomePage = showWelcomePage;
131103
this.disclaimerAcknowledged = disclaimerAcknowledged
132104
this.isFeatureDevEnabled = isFeatureDevEnabled
@@ -206,6 +178,8 @@ export class WebviewUIHandler {
206178
profileName
207179
})
208180

181+
this.featureConfigs = tryNewMap(featureConfigsSerialized)
182+
209183
// Set the new defaults for the quick action commands in all tabs now that isFeatureDevEnabled and isCodeTransformEnabled were enabled/disabled
210184
for (const tab of this.tabsStorage.getTabs()) {
211185
this.mynahUI?.updateStore(tab.id, {
@@ -238,7 +212,17 @@ export class WebviewUIHandler {
238212
onCWCOnboardingPageInteractionMessage: (message: ChatItem): string | undefined => {
239213
return this.messageController?.sendMessageToTab(message, 'cwc')
240214
},
215+
onQuickHandlerCommand: (tabID: string, command?: string, eventId?: string) => {
216+
this.tabsStorage.updateTabLastCommand(tabID, command)
217+
if (command === 'aws.awsq.transform') {
218+
this.quickActionHandler?.handleCommand({ command: '/transform' }, tabID, eventId)
219+
} else if (command === 'aws.awsq.clearchat') {
220+
this.quickActionHandler?.handleCommand({ command: '/clear' }, tabID)
221+
}
222+
},
241223
onCWCContextCommandMessage: (message: ChatItem, command?: string): string | undefined => {
224+
const selectedTab = this.tabsStorage.getSelectedTab()
225+
this.tabsStorage.updateTabLastCommand(selectedTab?.id || '', command || '')
242226
if (command === 'aws.amazonq.sendToPrompt') {
243227
return this.messageController?.sendSelectedCodeToTab(message)
244228
} else {
@@ -398,7 +382,7 @@ export class WebviewUIHandler {
398382
} as ChatItem)
399383
}
400384
},
401-
onChatAnswerReceived: (tabID: string, item: CWCChatItem) => {
385+
onChatAnswerReceived: (tabID: string, item: CWCChatItem, messageData: any) => {
402386
if (item.type === ChatItemType.ANSWER_PART || item.type === ChatItemType.CODE_RESULT) {
403387
this.mynahUI?.updateLastChatAnswer(tabID, {
404388
...(item.messageId !== undefined ? { messageId: item.messageId } : {}),
@@ -417,8 +401,12 @@ export class WebviewUIHandler {
417401
return
418402
}
419403

420-
if (item.body !== undefined || item.relatedContent !== undefined || item.followUp !== undefined) {
421-
this.mynahUI?.addChatItem(tabID, item)
404+
if (item.body !== undefined || item.relatedContent !== undefined || item.followUp !== undefined || item.formItems !== undefined || item.buttons !== undefined) {
405+
this.mynahUI?.addChatItem(tabID, {
406+
...item,
407+
messageId: item.messageId,
408+
codeBlockActions: this.getCodeBlockActions(messageData),
409+
})
422410
}
423411

424412
if (
@@ -543,7 +531,7 @@ export class WebviewUIHandler {
543531
this.tabsStorage.updateTabTypeFromUnknown(newTabID, tabType)
544532
this.connector?.onKnownTabOpen(newTabID)
545533
this.connector?.onUpdateTabType(newTabID)
546-
534+
this.featureConfigs = tryNewMap(featureConfigsSerialized)
547535
this.mynahUI?.updateStore(newTabID, this.tabDataGenerator!.getTabData(tabType, true))
548536
},
549537
onStartNewTransform: (tabID: string) => {
@@ -903,6 +891,43 @@ export class WebviewUIHandler {
903891
})
904892

905893
}
894+
895+
private getCodeBlockActions(messageData: any) {
896+
// Show ViewDiff and AcceptDiff for allowedCommands in CWC
897+
const isEnabled = this.featureConfigs?.get('ViewDiffInChat')?.variation === 'TREATMENT'
898+
const tab = this.tabsStorage.getTab(messageData?.tabID || '')
899+
const allowedCommands = [
900+
'aws.amazonq.refactorCode',
901+
'aws.amazonq.fixCode',
902+
'aws.amazonq.optimizeCode',
903+
'aws.amazonq.sendToPrompt',
904+
]
905+
if (isEnabled && tab?.type === 'cwc' && allowedCommands.includes(tab.lastCommand || '')) {
906+
return {
907+
'insert-to-cursor': undefined,
908+
accept_diff: {
909+
id: 'accept_diff',
910+
label: 'Apply Diff',
911+
icon: MynahIcons.OK_CIRCLED,
912+
data: messageData,
913+
},
914+
view_diff: {
915+
id: 'view_diff',
916+
label: 'View Diff',
917+
icon: MynahIcons.EYE,
918+
data: messageData,
919+
},
920+
}
921+
}
922+
// Show only "Copy" option for codeblocks in Q Test Tab
923+
if (tab?.type === 'testgen') {
924+
return {
925+
'insert-to-cursor': undefined,
926+
}
927+
}
928+
// Default will show "Copy" and "Insert at cursor" for codeblocks
929+
return {}
930+
}
906931
get mynahUI(): MynahUI | undefined {
907932
return this.mynahUIRef.mynahUI
908933
}

plugins/amazonq/mynah-ui/src/mynah-ui/ui/storages/tabsStorage.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ const TabTypes = [
1414
'agentWalkthrough',
1515
'welcome',
1616
'unknown',
17+
'testgen'
1718
] as const
1819
export type TabType = (typeof TabTypes)[number]
1920
export function isTabType(value: string): value is TabType {
@@ -45,6 +46,7 @@ export interface Tab {
4546
type: TabType
4647
isSelected: boolean
4748
openInteractionType?: TabOpenType
49+
lastCommand?: string
4850
}
4951

5052
export class TabsStorage {
@@ -93,6 +95,17 @@ export class TabsStorage {
9395
return this.tabs.get(tabID)?.status === 'dead'
9496
}
9597

98+
public updateTabLastCommand(tabID: string, command?: string) {
99+
if (command === undefined) {
100+
return
101+
}
102+
const currentTabValue = this.tabs.get(tabID)
103+
if (currentTabValue === undefined || currentTabValue.status === 'dead') {
104+
return
105+
}
106+
currentTabValue.lastCommand = command
107+
this.tabs.set(tabID, currentTabValue)
108+
}
96109
public updateTabStatus(tabID: string, tabStatus: TabStatus) {
97110
const currentTabValue = this.tabs.get(tabID)
98111
if (currentTabValue === undefined || currentTabValue.status === 'dead') {
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/*!
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
export interface FeatureValue {
6+
boolValue?: boolean;
7+
doubleValue?: number;
8+
longValue?: number;
9+
stringValue?: string;
10+
}
11+
12+
export class FeatureContext {
13+
constructor(
14+
public name: string,
15+
public variation: string,
16+
public value: FeatureValue
17+
) {}
18+
}
19+
20+
export function tryNewMap(arr: [string, FeatureContext][]) {
21+
try {
22+
return new Map(arr)
23+
} catch (error) {
24+
return new Map()
25+
}
26+
}

0 commit comments

Comments
 (0)