@@ -10,24 +10,57 @@ import com.intellij.openapi.application.runInEdt
1010import com.intellij.openapi.project.Project
1111import com.intellij.openapi.util.Disposer
1212import com.intellij.ui.components.JBLoadingPanel
13- import com.intellij.ui.components.JBPanelWithEmptyText
1413import com.intellij.ui.components.JBTextArea
1514import com.intellij.ui.components.panels.Wrapper
1615import com.intellij.ui.dsl.builder.Align
1716import com.intellij.ui.dsl.builder.AlignX
1817import com.intellij.ui.dsl.builder.AlignY
1918import com.intellij.ui.dsl.builder.panel
2019import com.intellij.ui.jcef.JBCefApp
20+ import kotlinx.coroutines.CoroutineScope
21+ import kotlinx.coroutines.launch
2122import software.aws.toolkits.jetbrains.isDeveloperMode
23+ import software.aws.toolkits.jetbrains.services.amazonq.apps.AmazonQAppInitContext
24+ import software.aws.toolkits.jetbrains.services.amazonq.apps.AppConnection
25+ import software.aws.toolkits.jetbrains.services.amazonq.commands.MessageTypeRegistry
2226import software.aws.toolkits.jetbrains.services.amazonq.lsp.artifacts.ArtifactHelper
27+ import software.aws.toolkits.jetbrains.services.amazonq.lsp.flareChat.AsyncChatUiListener
28+ import software.aws.toolkits.jetbrains.services.amazonq.messages.AmazonQMessage
29+ import software.aws.toolkits.jetbrains.services.amazonq.messages.MessageConnector
30+ import software.aws.toolkits.jetbrains.services.amazonq.profile.QRegionProfileManager
31+ import software.aws.toolkits.jetbrains.services.amazonq.util.highlightCommand
2332import software.aws.toolkits.jetbrains.services.amazonq.webview.Browser
24- import java.awt.event.ActionListener
33+ import software.aws.toolkits.jetbrains.services.amazonq.webview.BrowserConnector
34+ import software.aws.toolkits.jetbrains.services.amazonq.webview.FqnWebviewAdapter
35+ import software.aws.toolkits.jetbrains.services.amazonq.webview.theme.EditorThemeAdapter
36+ import software.aws.toolkits.jetbrains.services.amazonqCodeScan.auth.isCodeScanAvailable
37+ import software.aws.toolkits.jetbrains.services.amazonqCodeTest.auth.isCodeTestAvailable
38+ import software.aws.toolkits.jetbrains.services.amazonqDoc.auth.isDocAvailable
39+ import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.auth.isFeatureDevAvailable
40+ import software.aws.toolkits.jetbrains.services.codemodernizer.utils.isCodeTransformAvailable
2541import java.util.concurrent.CompletableFuture
2642import javax.swing.JButton
2743
28- class AmazonQPanel (private val parent : Disposable , val project : Project ) {
44+ class AmazonQPanel (val project : Project , private val scope : CoroutineScope ) : Disposable {
45+ private val browser = CompletableFuture <Browser >()
2946 private val webviewContainer = Wrapper ()
30- val browser = CompletableFuture <Browser >()
47+ private val appSource = AppSource ()
48+ private val browserConnector = BrowserConnector (project = project)
49+ private val editorThemeAdapter = EditorThemeAdapter ()
50+ private val appConnections = mutableListOf<AppConnection >()
51+
52+ init {
53+ project.messageBus.connect().subscribe(
54+ AsyncChatUiListener .TOPIC ,
55+ object : AsyncChatUiListener {
56+ override fun onChange (message : String ) {
57+ runInEdt {
58+ browser.get()?.postChat(message)
59+ }
60+ }
61+ }
62+ )
63+ }
3164
3265 val component = panel {
3366 row {
@@ -40,14 +73,12 @@ class AmazonQPanel(private val parent: Disposable, val project: Project) {
4073 row {
4174 cell(
4275 JButton (" Show Web Debugger" ).apply {
43- addActionListener(
44- ActionListener {
45- // Code to be executed when the button is clicked
46- // Add your logic here
47-
48- browser.get().jcefBrowser.openDevtools()
49- },
50- )
76+ addActionListener {
77+ // Code to be executed when the button is clicked
78+ // Add your logic here
79+
80+ browser.get().jcefBrowser.openDevtools()
81+ }
5182 },
5283 )
5384 .align(AlignX .CENTER )
@@ -57,19 +88,6 @@ class AmazonQPanel(private val parent: Disposable, val project: Project) {
5788 }
5889
5990 init {
60- init ()
61- }
62-
63- fun disposeAndRecreate () {
64- webviewContainer.removeAll()
65- val toDispose = browser.get()
66- init ()
67- if (toDispose != null ) {
68- Disposer .dispose(toDispose)
69- }
70- }
71-
72- private fun init () {
7391 if (! JBCefApp .isSupported()) {
7492 // Fallback to an alternative browser-less solution
7593 if (AppMode .isRemoteDevHost()) {
@@ -79,11 +97,10 @@ class AmazonQPanel(private val parent: Disposable, val project: Project) {
7997 }
8098 browser.complete(null )
8199 } else {
82- val loadingPanel = JBLoadingPanel (null , parent, 0 )
100+ val loadingPanel = JBLoadingPanel (null , this )
83101 val wrapper = Wrapper ()
84102 loadingPanel.startLoading()
85103
86- loadingPanel.add(JBPanelWithEmptyText ().withEmptyText(" Wait for chat to be ready" ))
87104 webviewContainer.add(wrapper)
88105 wrapper.setContent(loadingPanel)
89106
@@ -92,12 +109,93 @@ class AmazonQPanel(private val parent: Disposable, val project: Project) {
92109 loadingPanel.stopLoading()
93110 runInEdt {
94111 browser.complete(
95- Browser (parent , webUri, project).also {
112+ Browser (this , webUri, project).also {
96113 wrapper.setContent(it.component())
114+
115+ initConnections()
116+ connectUi(it)
117+ connectApps(it)
97118 }
98119 )
99120 }
100121 }
101122 }
102123 }
124+
125+ fun sendMessage (message : AmazonQMessage , tabType : String ) {
126+ appConnections.filter { it.app.tabTypes.contains(tabType) }.forEach {
127+ scope.launch {
128+ it.messagesFromUiToApp.publish(message)
129+ }
130+ }
131+ }
132+
133+ fun sendMessageAppToUi (message : AmazonQMessage , tabType : String ) {
134+ appConnections.filter { it.app.tabTypes.contains(tabType) }.forEach {
135+ scope.launch {
136+ it.messagesFromAppToUi.publish(message)
137+ }
138+ }
139+ }
140+
141+ private fun initConnections () {
142+ val apps = appSource.getApps(project)
143+ apps.forEach { app ->
144+ appConnections + = AppConnection (
145+ app = app,
146+ messagesFromAppToUi = MessageConnector (),
147+ messagesFromUiToApp = MessageConnector (),
148+ messageTypeRegistry = MessageTypeRegistry (),
149+ )
150+ }
151+ }
152+
153+ private fun connectApps (browser : Browser ) {
154+ val fqnWebviewAdapter = FqnWebviewAdapter (browser.jcefBrowser, browserConnector)
155+
156+ appConnections.forEach { connection ->
157+ val initContext = AmazonQAppInitContext (
158+ project = project,
159+ messagesFromAppToUi = connection.messagesFromAppToUi,
160+ messagesFromUiToApp = connection.messagesFromUiToApp,
161+ messageTypeRegistry = connection.messageTypeRegistry,
162+ fqnWebviewAdapter = fqnWebviewAdapter,
163+ )
164+ // Connect the app to the UI
165+ connection.app.init (initContext)
166+ // Dispose of the app when the tool window is disposed.
167+ Disposer .register(this , connection.app)
168+ }
169+ }
170+
171+ private fun connectUi (browser : Browser ) {
172+ browser.init (
173+ isCodeTransformAvailable = isCodeTransformAvailable(project),
174+ isFeatureDevAvailable = isFeatureDevAvailable(project),
175+ isCodeScanAvailable = isCodeScanAvailable(project),
176+ isCodeTestAvailable = isCodeTestAvailable(project),
177+ isDocAvailable = isDocAvailable(project),
178+ highlightCommand = highlightCommand(),
179+ activeProfile = QRegionProfileManager .getInstance().takeIf { it.shouldDisplayProfileInfo(project) }?.activeProfile(project)
180+ )
181+
182+ scope.launch {
183+ // Pipe messages from the UI to the relevant apps and vice versa
184+ browserConnector.connect(
185+ browser = browser,
186+ connections = appConnections,
187+ )
188+ }
189+
190+ scope.launch {
191+ // Update the theme in the UI when the IDE theme changes
192+ browserConnector.connectTheme(
193+ chatBrowser = browser.jcefBrowser.cefBrowser,
194+ themeSource = editorThemeAdapter.onThemeChange(),
195+ )
196+ }
197+ }
198+
199+ override fun dispose () {
200+ }
103201}
0 commit comments