Skip to content

Commit d0c9049

Browse files
rliLokeshDogga13
andauthored
merge: support chat on remote connections (#6008)
* feat(amazonq): Add support for Amazon Q chat on remote 242+ (#4825) * fix(amazonq): Bug Fix for "Q: Send to Prompt" doesn't work in split panes on local or remote (#5463) * fix(amazonq): use https as scheme for local webview resource resolver (#5489) https://developer.mozilla.org/en-US/docs/Web/API/Clipboard `navigator.clipboard` is only accessible from a "secure context", which JCEF does not think we are using, because the URI authority is not localhost or https. Since we are hooking onto the CEF resource request handler, it does not matter what we use for the scheme/authority as long as there is no conflict with real resources. Previous implementation used a different load mechanism which somehow met the secure context criteria. * fix(amazonq): fix agentic asset loading in remote mode (#5734) local uris cannot be loaded if the host is deemed non-remote * fix(amazonq): fix code issues tab not appearing in remote (#5737) on remote, each client gets its own instance of the tool window. logic needs to handle this case, otherwise it will only make changes against the 'main' IDE, and will not be visible to remote clients * fix(amazonq): use progress bar decorator while loading q chat on remote (#5749) animated icons do not show up on remote * fix: provide loading text when chat assets are slow to load (#5973) on remote, it can take 10+ seconds for chat to be visible. additionally, delete some unneeded assets to slightly reduce load time * feat(amazonq): limit remote to amzn-internal compute (#5993) Per internal discussion, feature deemed not ready for external users due to https://youtrack.jetbrains.com/issue/IJPL-203169 * fix: delete changelog for internal feature (#6009) * feat: show message to resize on remote chat (#6007) --------- Co-authored-by: Lokesh <[email protected]>
1 parent f221c06 commit d0c9049

File tree

64 files changed

+643
-1355
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

64 files changed

+643
-1355
lines changed

buildSrc/src/main/kotlin/software/aws/toolkits/gradle/intellij/IdeVersions.kt

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ object IdeVersions {
2222
private val commonPlugins = listOf(
2323
"Git4Idea",
2424
"org.jetbrains.plugins.terminal",
25-
"org.jetbrains.plugins.yaml"
25+
"org.jetbrains.plugins.yaml",
2626
)
2727

2828
private val ideProfiles = listOf(
@@ -34,6 +34,7 @@ object IdeVersions {
3434
"com.intellij.java",
3535
"com.intellij.gradle",
3636
"org.jetbrains.idea.maven",
37+
"com.jetbrains.codeWithMe",
3738
),
3839
marketplacePlugins = listOf(
3940
"org.toml.lang:242.20224.155",
@@ -71,6 +72,7 @@ object IdeVersions {
7172
"com.intellij.java",
7273
"com.intellij.gradle",
7374
"org.jetbrains.idea.maven",
75+
"com.jetbrains.codeWithMe",
7476
),
7577
marketplacePlugins = listOf(
7678
"org.toml.lang:243.21565.122",
@@ -114,6 +116,7 @@ object IdeVersions {
114116
"com.intellij.java",
115117
"com.intellij.gradle",
116118
"org.jetbrains.idea.maven",
119+
"com.jetbrains.codeWithMe",
117120
),
118121
marketplacePlugins = listOf(
119122
"org.toml.lang:251.26927.47",
@@ -160,6 +163,7 @@ object IdeVersions {
160163
"com.intellij.java",
161164
"com.intellij.gradle",
162165
"org.jetbrains.idea.maven",
166+
"com.jetbrains.codeWithMe",
163167
"com.intellij.properties"
164168
),
165169
marketplacePlugins = listOf(

buildSrc/src/main/kotlin/toolkit-publish-root-conventions.gradle.kts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
import org.jetbrains.intellij.platform.gradle.IntelliJPlatformType
55
import org.jetbrains.intellij.platform.gradle.tasks.PatchPluginXmlTask
6+
import org.jetbrains.intellij.platform.gradle.tasks.aware.SplitModeAware
67
import software.aws.toolkits.gradle.intellij.IdeFlavor
78
import software.aws.toolkits.gradle.intellij.toolkitIntelliJ
89

@@ -75,3 +76,8 @@ tasks.runIde {
7576
systemProperty("user.home", home)
7677
environment("HOME", home)
7778
}
79+
80+
val runSplitIde by intellijPlatformTesting.runIde.registering {
81+
splitMode = true
82+
splitModeTarget = SplitModeAware.SplitModeTarget.BACKEND
83+
}

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

Lines changed: 9 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ import kotlinx.coroutines.CoroutineScope
2222
import kotlinx.coroutines.flow.distinctUntilChanged
2323
import kotlinx.coroutines.flow.launchIn
2424
import kotlinx.coroutines.flow.onEach
25-
import org.cef.CefApp
2625
import software.aws.toolkits.core.utils.debug
2726
import software.aws.toolkits.core.utils.error
2827
import software.aws.toolkits.core.utils.getLogger
@@ -37,8 +36,8 @@ import software.aws.toolkits.jetbrains.core.credentials.sono.isSono
3736
import software.aws.toolkits.jetbrains.core.region.AwsRegionProvider
3837
import software.aws.toolkits.jetbrains.core.webview.BrowserMessage
3938
import software.aws.toolkits.jetbrains.core.webview.BrowserState
39+
import software.aws.toolkits.jetbrains.core.webview.LocalAssetJBCefRequestHandler
4040
import software.aws.toolkits.jetbrains.core.webview.LoginBrowser
41-
import software.aws.toolkits.jetbrains.core.webview.WebviewResourceHandlerFactory
4241
import software.aws.toolkits.jetbrains.isDeveloperMode
4342
import software.aws.toolkits.jetbrains.services.amazonq.profile.QProfileSwitchIntent
4443
import software.aws.toolkits.jetbrains.services.amazonq.profile.QRegionProfile
@@ -131,25 +130,14 @@ class QWebviewPanel private constructor(val project: Project, private val scope:
131130
class QWebviewBrowser(val project: Project, private val parentDisposable: Disposable) :
132131
LoginBrowser(
133132
project,
134-
QWebviewBrowser.DOMAIN,
135-
QWebviewBrowser.WEB_SCRIPT_URI
136133
),
137134
Disposable {
138135
// TODO: confirm if we need such configuration or the default is fine
139136
override val jcefBrowser = createBrowser(parentDisposable)
140137
private val query = JBCefJSQuery.create(jcefBrowser)
138+
private val assetHandler = LocalAssetJBCefRequestHandler(jcefBrowser)
141139

142140
init {
143-
CefApp.getInstance()
144-
.registerSchemeHandlerFactory(
145-
"http",
146-
domain,
147-
WebviewResourceHandlerFactory(
148-
domain = "http://$domain/",
149-
assetUri = "/webview/assets/"
150-
),
151-
)
152-
153141
loadWebView(query)
154142

155143
query.addHandler(jcefHandler)
@@ -332,7 +320,12 @@ class QWebviewBrowser(val project: Project, private val parentDisposable: Dispos
332320
}
333321

334322
override fun loadWebView(query: JBCefJSQuery) {
335-
jcefBrowser.loadHTML(getWebviewHTML(webScriptUri, query))
323+
val webScriptUri = assetHandler.createResource(
324+
WEB_SCRIPT,
325+
QWebviewBrowser::class.java.getResourceAsStream("/webview/assets/$WEB_SCRIPT")
326+
)
327+
328+
jcefBrowser.loadURL(assetHandler.createResource("content.html", getWebviewHTML(webScriptUri, query)))
336329
}
337330

338331
private fun handleListProfilesMessage() {
@@ -383,7 +376,6 @@ class QWebviewBrowser(val project: Project, private val parentDisposable: Dispos
383376

384377
companion object {
385378
private val LOG = getLogger<QWebviewBrowser>()
386-
private const val WEB_SCRIPT_URI = "http://webview/js/getStart.js"
387-
private const val DOMAIN = "webview"
379+
private const val WEB_SCRIPT = "js/getStart.js"
388380
}
389381
}

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

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,18 +11,20 @@ import icons.AwsIcons
1111
import software.aws.toolkits.jetbrains.services.amazonq.toolwindow.AMAZON_Q_WINDOW_ID
1212
import software.aws.toolkits.jetbrains.services.amazonq.toolwindow.AmazonQToolWindow
1313
import software.aws.toolkits.jetbrains.services.codewhisperer.util.CodeWhispererConstants.runScanKey
14-
import software.aws.toolkits.jetbrains.utils.isRunningOnRemoteBackend
1514
import software.aws.toolkits.resources.message
1615
import software.aws.toolkits.telemetry.UiTelemetry
1716

1817
class QOpenPanelAction : AnAction(message("action.q.openchat.text"), null, AwsIcons.Logos.AWS_Q) {
1918
override fun actionPerformed(e: AnActionEvent) {
20-
if (isRunningOnRemoteBackend()) return
2119
val project = e.getRequiredData(CommonDataKeys.PROJECT)
2220
UiTelemetry.click(project, "q_openChat")
2321
ToolWindowManager.getInstance(project).getToolWindow(AMAZON_Q_WINDOW_ID)?.activate(null, true)
2422
if (e.getData(runScanKey) == true) {
2523
AmazonQToolWindow.openScanTab(project)
2624
}
2725
}
26+
27+
override fun update(e: AnActionEvent) {
28+
e.presentation.isEnabled = e.getData(CommonDataKeys.PROJECT) != null
29+
}
2830
}

plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/gettingstarted/QGettingStartedContent.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import org.cef.browser.CefBrowser
1818
import org.cef.browser.CefFrame
1919
import org.cef.handler.CefLoadHandlerAdapter
2020
import software.aws.toolkits.jetbrains.core.coroutines.disposableCoroutineScope
21+
import software.aws.toolkits.jetbrains.core.webview.LocalAssetJBCefRequestHandler
2122
import software.aws.toolkits.jetbrains.services.amazonq.toolwindow.AmazonQToolWindow
2223
import software.aws.toolkits.jetbrains.services.amazonq.webview.theme.EditorThemeAdapter
2324
import software.aws.toolkits.resources.message
@@ -72,7 +73,7 @@ class QGettingStartedContent(val project: Project) : Disposable {
7273

7374
private fun loadWebView() {
7475
// load the web app
75-
jcefBrowser.loadHTML(getWebviewHTML())
76+
jcefBrowser.loadURL(LocalAssetJBCefRequestHandler(jcefBrowser).createResource("content.html", getWebviewHTML()))
7677
}
7778

7879
private fun getWebviewHTML(): String {

plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/toolwindow/AmazonQPanel.kt

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,13 @@
44
package software.aws.toolkits.jetbrains.services.amazonq.toolwindow
55

66
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
7-
import com.intellij.idea.AppMode
87
import com.intellij.openapi.Disposable
98
import com.intellij.openapi.components.service
109
import com.intellij.openapi.project.Project
1110
import com.intellij.openapi.util.Disposer
1211
import com.intellij.ui.components.JBLoadingPanel
1312
import com.intellij.ui.components.JBTextArea
13+
import com.intellij.ui.components.ProgressBarLoadingDecorator
1414
import com.intellij.ui.components.panels.Wrapper
1515
import com.intellij.ui.dsl.builder.Align
1616
import com.intellij.ui.dsl.builder.AlignX
@@ -45,6 +45,7 @@ import software.aws.toolkits.jetbrains.services.amazonqCodeTest.auth.isCodeTestA
4545
import software.aws.toolkits.jetbrains.services.amazonqDoc.auth.isDocAvailable
4646
import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.auth.isFeatureDevAvailable
4747
import software.aws.toolkits.jetbrains.services.codemodernizer.utils.isCodeTransformAvailable
48+
import software.aws.toolkits.jetbrains.utils.isRunningOnRemoteBackend
4849
import software.aws.toolkits.resources.message
4950
import java.awt.datatransfer.DataFlavor
5051
import java.awt.dnd.DropTarget
@@ -92,8 +93,8 @@ class AmazonQPanel(val project: Project, private val scope: CoroutineScope) : Di
9293
init {
9394
if (!JBCefApp.isSupported()) {
9495
// Fallback to an alternative browser-less solution
95-
if (AppMode.isRemoteDevHost()) {
96-
webviewContainer.add(JBTextArea("Amazon Q chat is not supported in remote dev environment."))
96+
if (isRunningOnRemoteBackend()) {
97+
webviewContainer.add(JBTextArea("Amazon Q chat is not supported in this remote dev environment because it lacks JCEF webview support."))
9798
} else {
9899
webviewContainer.add(JBTextArea("JCEF not supported"))
99100
}
@@ -102,21 +103,28 @@ class AmazonQPanel(val project: Project, private val scope: CoroutineScope) : Di
102103
webviewContainer.add(JBTextArea("${message("q.unavailable")}\n ${message("q.unavailable.node")}"))
103104
browser.complete(null)
104105
} else {
105-
val loadingPanel = JBLoadingPanel(null, this)
106+
val loadingPanel = if (isRunningOnRemoteBackend()) {
107+
JBLoadingPanel(null) {
108+
ProgressBarLoadingDecorator(it, this, -1)
109+
}
110+
} else {
111+
JBLoadingPanel(null, this)
112+
}
113+
106114
val wrapper = Wrapper()
107115
loadingPanel.startLoading()
108116

109117
webviewContainer.add(wrapper)
110118
wrapper.setContent(loadingPanel)
111119

112120
scope.launch {
113-
val webUri = service<ArtifactManager>().fetchArtifact(project).resolve("amazonq-ui.js").toUri()
121+
val mynahAsset = service<ArtifactManager>().fetchArtifact(project).resolve("amazonq-ui.js")
114122
// wait for server to be running
115123
AmazonQLspService.getInstance(project).instanceFlow.first()
116124

117125
withContext(EDT) {
118126
browser.complete(
119-
Browser(this@AmazonQPanel, webUri, project).also { browserInstance ->
127+
Browser(this@AmazonQPanel, mynahAsset, project).also { browserInstance ->
120128
wrapper.setContent(browserInstance.component())
121129

122130
// Register direct callback instead of using message bus

plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/toolwindow/AmazonQToolWindowFactory.kt

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ import software.aws.toolkits.jetbrains.core.credentials.ToolkitConnectionManager
2020
import software.aws.toolkits.jetbrains.core.credentials.pinning.QConnection
2121
import software.aws.toolkits.jetbrains.core.credentials.sso.bearer.BearerTokenAuthState
2222
import software.aws.toolkits.jetbrains.core.credentials.sso.bearer.BearerTokenProviderListener
23+
import software.aws.toolkits.jetbrains.core.notifications.BannerContent
24+
import software.aws.toolkits.jetbrains.core.notifications.NotificationDismissalState
2325
import software.aws.toolkits.jetbrains.core.notifications.NotificationPanel
2426
import software.aws.toolkits.jetbrains.core.notifications.ProcessNotificationsBase
2527
import software.aws.toolkits.jetbrains.core.webview.BrowserState
@@ -32,6 +34,8 @@ import software.aws.toolkits.jetbrains.services.amazonq.profile.QRegionProfileSe
3234
import software.aws.toolkits.jetbrains.utils.isQConnected
3335
import software.aws.toolkits.jetbrains.utils.isQExpired
3436
import software.aws.toolkits.jetbrains.utils.isQWebviewsAvailable
37+
import software.aws.toolkits.jetbrains.utils.isRunningOnRemoteBackend
38+
import software.aws.toolkits.resources.AmazonQBundle
3539
import software.aws.toolkits.resources.message
3640
import software.aws.toolkits.telemetry.FeatureId
3741
import java.awt.event.ComponentAdapter
@@ -42,7 +46,18 @@ class AmazonQToolWindowFactory : ToolWindowFactory, DumbAware {
4246
override fun createToolWindowContent(project: Project, toolWindow: ToolWindow) {
4347
val mainPanel = BorderLayoutPanel()
4448
val qPanel = Wrapper()
45-
val notificationPanel = NotificationPanel()
49+
val notificationPanel = NotificationPanel().apply {
50+
if (isRunningOnRemoteBackend() && !NotificationDismissalState.getInstance().isDismissed(REMOTE_RESIZE_NOTIFICATION_ID)) {
51+
updateNotificationPanel(
52+
BannerContent(
53+
REMOTE_RESIZE_NOTIFICATION_ID,
54+
AmazonQBundle.message("amazonq.resize.panel"),
55+
// message is not used for banner
56+
"",
57+
)
58+
)
59+
}
60+
}
4661

4762
mainPanel.addToTop(notificationPanel)
4863
mainPanel.add(qPanel)
@@ -133,8 +148,12 @@ class AmazonQToolWindowFactory : ToolWindowFactory, DumbAware {
133148
}
134149
}
135150

151+
/**
152+
* Only applies to local
153+
* On remote, since we are using PROJECTOR_INSTANCING, this will never run
154+
*/
136155
override fun init(toolWindow: ToolWindow) {
137-
toolWindow.stripeTitle = message("q.window.title")
156+
toolWindow.stripeTitle = message("toolwindow.stripe.amazon.q.window")
138157
toolWindow.component.addComponentListener(
139158
object : ComponentAdapter() {
140159
override fun componentResized(e: ComponentEvent) {
@@ -143,6 +162,8 @@ class AmazonQToolWindowFactory : ToolWindowFactory, DumbAware {
143162
LOG.debug {
144163
"Amazon Q Tool window stretched to a width less than the minimum allowed width, resizing to the minimum allowed width"
145164
}
165+
166+
// can't implement equivalent on remote as stretchWidth impl is noop
146167
(toolWindow as ToolWindowEx).stretchWidth(MINIMUM_TOOLWINDOW_WIDTH - newWidth)
147168
}
148169
}
@@ -155,5 +176,6 @@ class AmazonQToolWindowFactory : ToolWindowFactory, DumbAware {
155176
private val LOG = getLogger<AmazonQToolWindowFactory>()
156177
const val WINDOW_ID = AMAZON_Q_WINDOW_ID
157178
private const val MINIMUM_TOOLWINDOW_WIDTH = 325
179+
private const val REMOTE_RESIZE_NOTIFICATION_ID = "resize.amazon.q.toolwindow.if.remote"
158180
}
159181
}

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

Lines changed: 0 additions & 96 deletions
This file was deleted.

0 commit comments

Comments
 (0)