Skip to content

Commit ffcd74d

Browse files
fix(amazonq): right click options dont send context if chat not loaded
Problem: When a user starts up VSC, hasn't opened Q chat yet, then highlights text and does a right click option like "send to prompt" THEN the whole prompt fails to make it to chat. This is because there is a race condition between the message being sent to the webview, and the webview completed loading and ready to recieve messages. Solution: In the appToMessage publisher, verify that the chat is ready to recieve messages based on if the webview sent the chat ui ready message. Otherwise wait until we get it, and then send the message. Signed-off-by: nkomonen-amazon <[email protected]>
1 parent 9dffe3e commit ffcd74d

File tree

4 files changed

+60
-4
lines changed

4 files changed

+60
-4
lines changed

packages/core/src/amazonq/apps/initContext.ts

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,25 @@
44
*/
55

66
import { EventEmitter } from 'vscode'
7-
import { MessagePublisher } from '../messages/messagePublisher'
7+
import { MessagePublisher, UiReadyMessagePublisher } from '../messages/messagePublisher'
88
import { MessageListener } from '../messages/messageListener'
99
import { TabType } from '../webview/ui/storages/tabsStorage'
1010

1111
export interface AmazonQAppInitContext {
1212
registerWebViewToAppMessagePublisher(eventEmitter: MessagePublisher<any>, tabType: TabType): void
1313
getAppsToWebViewMessagePublisher(): MessagePublisher<any>
1414
onDidChangeAmazonQVisibility: EventEmitter<boolean>
15+
setAmazonQUiReady: () => void
1516
}
1617

1718
export class DefaultAmazonQAppInitContext implements AmazonQAppInitContext {
19+
private _isUiReady: boolean = false
1820
private readonly appsToWebViewEventEmitter = new EventEmitter<any>()
1921
private readonly appsToWebViewMessageListener = new MessageListener<any>(this.appsToWebViewEventEmitter)
20-
private readonly appsToWebViewMessagePublisher = new MessagePublisher<any>(this.appsToWebViewEventEmitter)
22+
private readonly appsToWebViewMessagePublisher = new UiReadyMessagePublisher<any>(
23+
this.appsToWebViewEventEmitter,
24+
() => this._isUiReady
25+
) // this should know the state of the webview so it can sleep until webview is loaded, then send message
2126
private readonly webViewToAppsMessagePublishers: Map<TabType, MessagePublisher<any>> = new Map()
2227
public readonly onDidChangeAmazonQVisibility = new EventEmitter<boolean>()
2328

@@ -44,4 +49,11 @@ export class DefaultAmazonQAppInitContext implements AmazonQAppInitContext {
4449
getAppsToWebViewMessagePublisher(): MessagePublisher<any> {
4550
return this.appsToWebViewMessagePublisher
4651
}
52+
53+
/**
54+
* Indicate the Q Chat UI is ready to recieve messages
55+
*/
56+
setAmazonQUiReady(): void {
57+
this._isUiReady = true
58+
}
4759
}

packages/core/src/amazonq/messages/messagePublisher.ts

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
*/
55

66
import { EventEmitter } from 'vscode'
7+
import { getLogger, waitUntil } from '../../shared'
78

89
export class MessagePublisher<T> {
910
constructor(private readonly eventEmitter: EventEmitter<T>) {}
@@ -12,3 +13,45 @@ export class MessagePublisher<T> {
1213
this.eventEmitter.fire(event)
1314
}
1415
}
16+
17+
/**
18+
* Same as {@link MessagePublisher}, but will wait until the UI indicates it
19+
* is ready to recieve messages, before the message is published.
20+
*
21+
* This solves a problem when running a right click menu option like
22+
* "Send To Prompt" BUT chat is not opened yet, it would result in the prompt failing to
23+
* be recieved by chat.
24+
*/
25+
export class UiReadyMessagePublisher<T> extends MessagePublisher<T> {
26+
constructor(
27+
eventEmitter: EventEmitter<T>,
28+
private readonly isChatUiReady: () => boolean
29+
) {
30+
super(eventEmitter)
31+
}
32+
public override publish(event: T): void {
33+
// immediately send if Chat UI is ready
34+
if (this.isChatUiReady()) {
35+
super.publish(event)
36+
return
37+
}
38+
// Otherwise wait, since it is probably still loading
39+
void waitUntil(
40+
async () => {
41+
if (this.isChatUiReady()) {
42+
super.publish(event)
43+
return true
44+
}
45+
return false
46+
},
47+
{
48+
timeout: 5000,
49+
interval: 100,
50+
}
51+
).then((res) => {
52+
if (!res) {
53+
getLogger('chat').error('Message publisher timed out waiting for UI to be ready')
54+
}
55+
})
56+
}
57+
}

packages/core/src/amazonq/webview/messages/messageDispatcher.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import { telemetry } from '../../../shared/telemetry'
1313
import { AmazonQChatMessageDuration } from '../../messages/chatMessageDuration'
1414
import { globals, openUrl } from '../../../shared'
1515
import { isClickTelemetry, isOpenAgentTelemetry } from '../ui/telemetry/actions'
16+
import { DefaultAmazonQAppInitContext } from '../../apps/initContext'
1617

1718
export function dispatchWebViewMessagesToApps(
1819
webview: Webview,
@@ -21,12 +22,12 @@ export function dispatchWebViewMessagesToApps(
2122
webview.onDidReceiveMessage((msg) => {
2223
switch (msg.command) {
2324
case 'ui-is-ready': {
25+
DefaultAmazonQAppInitContext.instance.setAmazonQUiReady()
2426
/**
2527
* ui-is-ready isn't associated to any tab so just record the telemetry event and continue.
2628
* This would be equivalent of the duration between "user clicked open q" and "ui has become available"
2729
* NOTE: Amazon Q UI is only loaded ONCE. The state is saved between each hide/show of the webview.
2830
*/
29-
3031
telemetry.webview_load.emit({
3132
webviewName: 'amazonq',
3233
duration: performance.measure(amazonqMark.uiReady, amazonqMark.open).duration,

packages/core/src/shared/logger/logger.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
import * as vscode from 'vscode'
77

8-
export type LogTopic = 'crashMonitoring' | 'dev/beta' | 'notifications' | 'test' | 'childProcess' | 'unknown'
8+
export type LogTopic = 'crashMonitoring' | 'dev/beta' | 'notifications' | 'test' | 'childProcess' | 'unknown' | 'chat'
99

1010
class ErrorLog {
1111
constructor(

0 commit comments

Comments
 (0)