@@ -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