@@ -7,27 +7,62 @@ import com.intellij.idea.AppMode
77import com.intellij.openapi.Disposable
88import com.intellij.openapi.application.ApplicationManager
99import com.intellij.openapi.application.runInEdt
10+ import com.intellij.openapi.components.service
1011import com.intellij.openapi.project.Project
1112import com.intellij.openapi.util.Disposer
1213import com.intellij.ui.components.JBLoadingPanel
13- import com.intellij.ui.components.JBPanelWithEmptyText
1414import com.intellij.ui.components.JBTextArea
1515import com.intellij.ui.components.panels.Wrapper
1616import com.intellij.ui.dsl.builder.Align
1717import com.intellij.ui.dsl.builder.AlignX
1818import com.intellij.ui.dsl.builder.AlignY
1919import com.intellij.ui.dsl.builder.panel
2020import com.intellij.ui.jcef.JBCefApp
21+ import kotlinx.coroutines.CoroutineScope
22+ import kotlinx.coroutines.launch
23+ import kotlinx.coroutines.runBlocking
2124import software.aws.toolkits.jetbrains.isDeveloperMode
22- import software.aws.toolkits.jetbrains.services.amazonq.lsp.artifacts.ArtifactHelper
25+ import software.aws.toolkits.jetbrains.services.amazonq.apps.AmazonQAppInitContext
26+ import software.aws.toolkits.jetbrains.services.amazonq.apps.AppConnection
27+ import software.aws.toolkits.jetbrains.services.amazonq.commands.MessageTypeRegistry
28+ import software.aws.toolkits.jetbrains.services.amazonq.lsp.artifacts.ArtifactManager
29+ import software.aws.toolkits.jetbrains.services.amazonq.lsp.flareChat.AsyncChatUiListener
30+ import software.aws.toolkits.jetbrains.services.amazonq.messages.AmazonQMessage
31+ import software.aws.toolkits.jetbrains.services.amazonq.messages.MessageConnector
32+ import software.aws.toolkits.jetbrains.services.amazonq.profile.QRegionProfileManager
33+ import software.aws.toolkits.jetbrains.services.amazonq.util.highlightCommand
2334import software.aws.toolkits.jetbrains.services.amazonq.webview.Browser
24- import java.awt.event.ActionListener
35+ import software.aws.toolkits.jetbrains.services.amazonq.webview.BrowserConnector
36+ import software.aws.toolkits.jetbrains.services.amazonq.webview.FqnWebviewAdapter
37+ import software.aws.toolkits.jetbrains.services.amazonq.webview.theme.EditorThemeAdapter
38+ import software.aws.toolkits.jetbrains.services.amazonqCodeScan.auth.isCodeScanAvailable
39+ import software.aws.toolkits.jetbrains.services.amazonqCodeTest.auth.isCodeTestAvailable
40+ import software.aws.toolkits.jetbrains.services.amazonqDoc.auth.isDocAvailable
41+ import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.auth.isFeatureDevAvailable
42+ import software.aws.toolkits.jetbrains.services.codemodernizer.utils.isCodeTransformAvailable
2543import java.util.concurrent.CompletableFuture
2644import javax.swing.JButton
2745
28- class AmazonQPanel (private val parent : Disposable , val project : Project ) {
46+ class AmazonQPanel (val project : Project , private val scope : CoroutineScope ) : Disposable {
47+ private val browser = CompletableFuture <Browser >()
2948 private val webviewContainer = Wrapper ()
30- val browser = CompletableFuture <Browser >()
49+ private val appSource = AppSource ()
50+ private val browserConnector = BrowserConnector (project = project)
51+ private val editorThemeAdapter = EditorThemeAdapter ()
52+ private val appConnections = mutableListOf<AppConnection >()
53+
54+ init {
55+ project.messageBus.connect().subscribe(
56+ AsyncChatUiListener .TOPIC ,
57+ object : AsyncChatUiListener {
58+ override fun onChange (message : String ) {
59+ runInEdt {
60+ browser.get()?.postChat(message)
61+ }
62+ }
63+ }
64+ )
65+ }
3166
3267 val component = panel {
3368 row {
@@ -40,14 +75,12 @@ class AmazonQPanel(private val parent: Disposable, val project: Project) {
4075 row {
4176 cell(
4277 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- )
78+ addActionListener {
79+ // Code to be executed when the button is clicked
80+ // Add your logic here
81+
82+ browser.get().jcefBrowser.openDevtools()
83+ }
5184 },
5285 )
5386 .align(AlignX .CENTER )
@@ -57,19 +90,6 @@ class AmazonQPanel(private val parent: Disposable, val project: Project) {
5790 }
5891
5992 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 () {
7393 if (! JBCefApp .isSupported()) {
7494 // Fallback to an alternative browser-less solution
7595 if (AppMode .isRemoteDevHost()) {
@@ -79,25 +99,105 @@ class AmazonQPanel(private val parent: Disposable, val project: Project) {
7999 }
80100 browser.complete(null )
81101 } else {
82- val loadingPanel = JBLoadingPanel (null , parent, 0 )
102+ val loadingPanel = JBLoadingPanel (null , this )
83103 val wrapper = Wrapper ()
84104 loadingPanel.startLoading()
85105
86- loadingPanel.add(JBPanelWithEmptyText ().withEmptyText(" Wait for chat to be ready" ))
87106 webviewContainer.add(wrapper)
88107 wrapper.setContent(loadingPanel)
89108
90109 ApplicationManager .getApplication().executeOnPooledThread {
91- val webUri = ArtifactHelper ().getLatestLocalLspArtifact( ).resolve(" amazonq-ui.js" ).toUri()
110+ val webUri = runBlocking { service< ArtifactManager > ().fetchArtifact(project ).resolve(" amazonq-ui.js" ).toUri() }
92111 loadingPanel.stopLoading()
93112 runInEdt {
94113 browser.complete(
95- Browser (parent , webUri, project).also {
114+ Browser (this , webUri, project).also {
96115 wrapper.setContent(it.component())
116+
117+ initConnections()
118+ connectUi(it)
119+ connectApps(it)
97120 }
98121 )
99122 }
100123 }
101124 }
102125 }
126+
127+ fun sendMessage (message : AmazonQMessage , tabType : String ) {
128+ appConnections.filter { it.app.tabTypes.contains(tabType) }.forEach {
129+ scope.launch {
130+ it.messagesFromUiToApp.publish(message)
131+ }
132+ }
133+ }
134+
135+ fun sendMessageAppToUi (message : AmazonQMessage , tabType : String ) {
136+ appConnections.filter { it.app.tabTypes.contains(tabType) }.forEach {
137+ scope.launch {
138+ it.messagesFromAppToUi.publish(message)
139+ }
140+ }
141+ }
142+
143+ private fun initConnections () {
144+ val apps = appSource.getApps(project)
145+ apps.forEach { app ->
146+ appConnections + = AppConnection (
147+ app = app,
148+ messagesFromAppToUi = MessageConnector (),
149+ messagesFromUiToApp = MessageConnector (),
150+ messageTypeRegistry = MessageTypeRegistry (),
151+ )
152+ }
153+ }
154+
155+ private fun connectApps (browser : Browser ) {
156+ val fqnWebviewAdapter = FqnWebviewAdapter (browser.jcefBrowser, browserConnector)
157+
158+ appConnections.forEach { connection ->
159+ val initContext = AmazonQAppInitContext (
160+ project = project,
161+ messagesFromAppToUi = connection.messagesFromAppToUi,
162+ messagesFromUiToApp = connection.messagesFromUiToApp,
163+ messageTypeRegistry = connection.messageTypeRegistry,
164+ fqnWebviewAdapter = fqnWebviewAdapter,
165+ )
166+ // Connect the app to the UI
167+ connection.app.init (initContext)
168+ // Dispose of the app when the tool window is disposed.
169+ Disposer .register(this , connection.app)
170+ }
171+ }
172+
173+ private fun connectUi (browser : Browser ) {
174+ browser.init (
175+ isCodeTransformAvailable = isCodeTransformAvailable(project),
176+ isFeatureDevAvailable = isFeatureDevAvailable(project),
177+ isCodeScanAvailable = isCodeScanAvailable(project),
178+ isCodeTestAvailable = isCodeTestAvailable(project),
179+ isDocAvailable = isDocAvailable(project),
180+ highlightCommand = highlightCommand(),
181+ activeProfile = QRegionProfileManager .getInstance().takeIf { it.shouldDisplayProfileInfo(project) }?.activeProfile(project)
182+ )
183+
184+ scope.launch {
185+ // Pipe messages from the UI to the relevant apps and vice versa
186+ browserConnector.connect(
187+ browser = browser,
188+ connections = appConnections,
189+ )
190+ }
191+
192+ scope.launch {
193+ // Update the theme in the UI when the IDE theme changes
194+ browserConnector.connectTheme(
195+ chatBrowser = browser.jcefBrowser.cefBrowser,
196+ themeSource = editorThemeAdapter.onThemeChange(),
197+ )
198+ }
199+ }
200+
201+ override fun dispose () {
202+ }
103203}
0 commit comments