diff --git a/buildSrc/src/main/kotlin/toolkit-publish-root-conventions.gradle.kts b/buildSrc/src/main/kotlin/toolkit-publish-root-conventions.gradle.kts index 2e4d45b570..d7b079b116 100644 --- a/buildSrc/src/main/kotlin/toolkit-publish-root-conventions.gradle.kts +++ b/buildSrc/src/main/kotlin/toolkit-publish-root-conventions.gradle.kts @@ -3,6 +3,7 @@ import org.jetbrains.intellij.platform.gradle.IntelliJPlatformType import org.jetbrains.intellij.platform.gradle.tasks.PatchPluginXmlTask +import org.jetbrains.intellij.platform.gradle.tasks.aware.SplitModeAware import software.aws.toolkits.gradle.intellij.IdeFlavor import software.aws.toolkits.gradle.intellij.toolkitIntelliJ @@ -75,3 +76,8 @@ tasks.runIde { systemProperty("user.home", home) environment("HOME", home) } + +val runSplitIde by intellijPlatformTesting.runIde.registering { + splitMode = true + splitModeTarget = SplitModeAware.SplitModeTarget.BACKEND +} diff --git a/plugins/amazonq/build.gradle.kts b/plugins/amazonq/build.gradle.kts index f3caa6e678..4985f69e76 100644 --- a/plugins/amazonq/build.gradle.kts +++ b/plugins/amazonq/build.gradle.kts @@ -1,11 +1,7 @@ // Copyright 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import org.jetbrains.intellij.platform.gradle.IntelliJPlatformType import software.aws.toolkits.gradle.changelog.tasks.GeneratePluginChangeLog -import software.aws.toolkits.gradle.intellij.IdeFlavor -import software.aws.toolkits.gradle.intellij.IdeVersions -import software.aws.toolkits.gradle.intellij.toolkitIntelliJ plugins { id("toolkit-publishing-conventions") diff --git a/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/QLoginWebview.kt b/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/QLoginWebview.kt index bc594209fb..a314ea8d49 100644 --- a/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/QLoginWebview.kt +++ b/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/QLoginWebview.kt @@ -16,7 +16,6 @@ import com.intellij.ui.components.panels.Wrapper import com.intellij.ui.dsl.builder.Align import com.intellij.ui.dsl.builder.panel import com.intellij.ui.jcef.JBCefJSQuery -import org.cef.CefApp import software.aws.toolkits.core.utils.error import software.aws.toolkits.core.utils.getLogger import software.aws.toolkits.core.utils.warn @@ -30,8 +29,8 @@ import software.aws.toolkits.jetbrains.core.credentials.sono.isSono import software.aws.toolkits.jetbrains.core.region.AwsRegionProvider import software.aws.toolkits.jetbrains.core.webview.BrowserMessage import software.aws.toolkits.jetbrains.core.webview.BrowserState +import software.aws.toolkits.jetbrains.core.webview.LocalAssetJBCefRequestHandler import software.aws.toolkits.jetbrains.core.webview.LoginBrowser -import software.aws.toolkits.jetbrains.core.webview.WebviewResourceHandlerFactory import software.aws.toolkits.jetbrains.isDeveloperMode import software.aws.toolkits.jetbrains.services.amazonq.util.createBrowser import software.aws.toolkits.jetbrains.utils.isQConnected @@ -108,25 +107,14 @@ class QWebviewPanel private constructor(val project: Project) : Disposable { class QWebviewBrowser(val project: Project, private val parentDisposable: Disposable) : LoginBrowser( project, - QWebviewBrowser.DOMAIN, - QWebviewBrowser.WEB_SCRIPT_URI ), Disposable { // TODO: confirm if we need such configuration or the default is fine override val jcefBrowser = createBrowser(parentDisposable) private val query = JBCefJSQuery.create(jcefBrowser) + private val assetHandler = LocalAssetJBCefRequestHandler(jcefBrowser) init { - CefApp.getInstance() - .registerSchemeHandlerFactory( - "http", - domain, - WebviewResourceHandlerFactory( - domain = "http://$domain/", - assetUri = "/webview/assets/" - ), - ) - loadWebView(query) query.addHandler(jcefHandler) @@ -273,12 +261,15 @@ class QWebviewBrowser(val project: Project, private val parentDisposable: Dispos } override fun loadWebView(query: JBCefJSQuery) { - jcefBrowser.loadHTML(getWebviewHTML(webScriptUri, query)) + val webScriptUri = assetHandler.createResource( + "js/getStart.js", + QWebviewBrowser::class.java.getResourceAsStream("/webview/assets/js/getStart.js") + ) + + jcefBrowser.loadURL(assetHandler.createResource("content.html", getWebviewHTML(webScriptUri, query))) } companion object { private val LOG = getLogger() - private const val WEB_SCRIPT_URI = "http://webview/js/getStart.js" - private const val DOMAIN = "webview" } } diff --git a/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/QOpenPanelAction.kt b/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/QOpenPanelAction.kt index de01e99bad..cab5e06d31 100644 --- a/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/QOpenPanelAction.kt +++ b/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/QOpenPanelAction.kt @@ -11,13 +11,11 @@ import icons.AwsIcons import software.aws.toolkits.jetbrains.services.amazonq.toolwindow.AMAZON_Q_WINDOW_ID import software.aws.toolkits.jetbrains.services.amazonq.toolwindow.AmazonQToolWindow import software.aws.toolkits.jetbrains.services.codewhisperer.util.CodeWhispererConstants.runScanKey -import software.aws.toolkits.jetbrains.utils.isRunningOnRemoteBackend import software.aws.toolkits.resources.message import software.aws.toolkits.telemetry.UiTelemetry class QOpenPanelAction : AnAction(message("action.q.openchat.text"), null, AwsIcons.Logos.AWS_Q) { override fun actionPerformed(e: AnActionEvent) { - if (isRunningOnRemoteBackend()) return val project = e.getRequiredData(CommonDataKeys.PROJECT) UiTelemetry.click(project, "q_openChat") ToolWindowManager.getInstance(project).getToolWindow(AMAZON_Q_WINDOW_ID)?.activate(null, true) @@ -25,4 +23,8 @@ class QOpenPanelAction : AnAction(message("action.q.openchat.text"), null, AwsIc AmazonQToolWindow.openScanTab(project) } } + + override fun update(e: AnActionEvent) { + e.presentation.isEnabled = e.getData(CommonDataKeys.PROJECT) != null + } } diff --git a/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/gettingstarted/QGettingStartedContent.kt b/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/gettingstarted/QGettingStartedContent.kt index afb490b9f1..3e81b2c836 100644 --- a/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/gettingstarted/QGettingStartedContent.kt +++ b/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/gettingstarted/QGettingStartedContent.kt @@ -18,6 +18,7 @@ import org.cef.browser.CefBrowser import org.cef.browser.CefFrame import org.cef.handler.CefLoadHandlerAdapter import software.aws.toolkits.jetbrains.core.coroutines.disposableCoroutineScope +import software.aws.toolkits.jetbrains.core.webview.LocalAssetJBCefRequestHandler import software.aws.toolkits.jetbrains.services.amazonq.toolwindow.AmazonQToolWindow import software.aws.toolkits.jetbrains.services.amazonq.webview.theme.EditorThemeAdapter import software.aws.toolkits.resources.message @@ -72,7 +73,7 @@ class QGettingStartedContent(val project: Project) : Disposable { private fun loadWebView() { // load the web app - jcefBrowser.loadHTML(getWebviewHTML()) + jcefBrowser.loadURL(LocalAssetJBCefRequestHandler(jcefBrowser).createResource("content.html", getWebviewHTML())) } private fun getWebviewHTML(): String { diff --git a/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/toolwindow/AmazonQPanel.kt b/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/toolwindow/AmazonQPanel.kt index 396057bc9a..a0e18e1038 100644 --- a/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/toolwindow/AmazonQPanel.kt +++ b/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/toolwindow/AmazonQPanel.kt @@ -3,7 +3,6 @@ package software.aws.toolkits.jetbrains.services.amazonq.toolwindow -import com.intellij.idea.AppMode import com.intellij.openapi.Disposable import com.intellij.openapi.util.Disposer import com.intellij.ui.components.JBTextArea @@ -15,6 +14,7 @@ import com.intellij.ui.dsl.builder.panel import com.intellij.ui.jcef.JBCefApp import software.aws.toolkits.jetbrains.isDeveloperMode import software.aws.toolkits.jetbrains.services.amazonq.webview.Browser +import software.aws.toolkits.jetbrains.utils.isRunningOnRemoteBackend import java.awt.event.ActionListener import javax.swing.JButton @@ -66,8 +66,8 @@ class AmazonQPanel(private val parent: Disposable) { private fun init() { if (!JBCefApp.isSupported()) { // Fallback to an alternative browser-less solution - if (AppMode.isRemoteDevHost()) { - webviewContainer.add(JBTextArea("Amazon Q chat is not supported in remote dev environment.")) + if (isRunningOnRemoteBackend()) { + webviewContainer.add(JBTextArea("Amazon Q chat is not supported in this remote dev environment because it lacks JCEF webview support.")) } else { webviewContainer.add(JBTextArea("JCEF not supported")) } diff --git a/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/webview/Browser.kt b/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/webview/Browser.kt index cc39985dd3..b51ebec12a 100644 --- a/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/webview/Browser.kt +++ b/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/webview/Browser.kt @@ -7,10 +7,11 @@ import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper import com.intellij.openapi.Disposable import com.intellij.openapi.util.Disposer import com.intellij.ui.jcef.JBCefJSQuery -import org.cef.CefApp +import software.aws.toolkits.jetbrains.core.webview.LocalAssetJBCefRequestHandler import software.aws.toolkits.jetbrains.services.amazonq.util.HighlightCommand import software.aws.toolkits.jetbrains.services.amazonq.util.createBrowser import software.aws.toolkits.jetbrains.settings.MeetQSettings +import java.nio.file.Paths /* Displays the web view for the Amazon Q tool window @@ -21,6 +22,17 @@ class Browser(parent: Disposable) : Disposable { val receiveMessageQuery = JBCefJSQuery.create(jcefBrowser) + private val assetRequestHandler = LocalAssetJBCefRequestHandler(jcefBrowser) + + init { + assetRequestHandler.addWildcardHandler("mynah") { path -> + val asset = path.replaceFirst("mynah/", "/mynah-ui/assets/") + Paths.get(asset).normalize().toString().let { + this::class.java.getResourceAsStream(it) + } + } + } + fun init( isCodeTransformAvailable: Boolean, isFeatureDevAvailable: Boolean, @@ -29,14 +41,6 @@ class Browser(parent: Disposable) : Disposable { isCodeTestAvailable: Boolean, highlightCommand: HighlightCommand?, ) { - // register the scheme handler to route http://mynah/ URIs to the resources/assets directory on classpath - CefApp.getInstance() - .registerSchemeHandlerFactory( - "http", - "mynah", - AssetResourceHandler.AssetResourceHandlerFactory(), - ) - loadWebView(isCodeTransformAvailable, isFeatureDevAvailable, isDocAvailable, isCodeScanAvailable, isCodeTestAvailable, highlightCommand) } @@ -63,9 +67,13 @@ class Browser(parent: Disposable) : Disposable { // setup empty state. The message request handlers use this for storing state // that's persistent between page loads. jcefBrowser.setProperty("state", "") + // load the web app - jcefBrowser.loadHTML( - getWebviewHTML(isCodeTransformAvailable, isFeatureDevAvailable, isDocAvailable, isCodeScanAvailable, isCodeTestAvailable, highlightCommand) + jcefBrowser.loadURL( + assetRequestHandler.createResource( + "webview/chat.html", + getWebviewHTML(isCodeTransformAvailable, isFeatureDevAvailable, isDocAvailable, isCodeScanAvailable, isCodeTestAvailable, highlightCommand) + ) ) } @@ -84,7 +92,7 @@ class Browser(parent: Disposable) : Disposable { val postMessageToJavaJsCode = receiveMessageQuery.inject("JSON.stringify(message)") val jsScripts = """ - +