@@ -11,7 +11,6 @@ import com.intellij.openapi.components.service
1111import com.intellij.openapi.project.Project
1212import com.intellij.openapi.util.Disposer
1313import com.intellij.ui.components.JBLoadingPanel
14- import com.intellij.ui.components.JBPanelWithEmptyText
1514import com.intellij.ui.components.JBTextArea
1615import com.intellij.ui.components.panels.Wrapper
1716import com.intellij.ui.dsl.builder.Align
@@ -20,17 +19,50 @@ import com.intellij.ui.dsl.builder.AlignY
2019import com.intellij.ui.dsl.builder.panel
2120import com.intellij.ui.jcef.JBCefApp
2221import kotlinx.coroutines.runBlocking
22+ import kotlinx.coroutines.CoroutineScope
23+ import kotlinx.coroutines.launch
2324import software.aws.toolkits.jetbrains.isDeveloperMode
24- 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
2528import 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
2634import software.aws.toolkits.jetbrains.services.amazonq.webview.Browser
27- 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
2843import java.util.concurrent.CompletableFuture
2944import javax.swing.JButton
3045
31- 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 >()
3248 private val webviewContainer = Wrapper ()
33- 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+ }
3466
3567 val component = panel {
3668 row {
@@ -43,14 +75,12 @@ class AmazonQPanel(private val parent: Disposable, val project: Project) {
4375 row {
4476 cell(
4577 JButton (" Show Web Debugger" ).apply {
46- addActionListener(
47- ActionListener {
48- // Code to be executed when the button is clicked
49- // Add your logic here
50-
51- browser.get().jcefBrowser.openDevtools()
52- },
53- )
78+ addActionListener {
79+ // Code to be executed when the button is clicked
80+ // Add your logic here
81+
82+ browser.get().jcefBrowser.openDevtools()
83+ }
5484 },
5585 )
5686 .align(AlignX .CENTER )
@@ -60,19 +90,6 @@ class AmazonQPanel(private val parent: Disposable, val project: Project) {
6090 }
6191
6292 init {
63- init ()
64- }
65-
66- fun disposeAndRecreate () {
67- webviewContainer.removeAll()
68- val toDispose = browser.get()
69- init ()
70- if (toDispose != null ) {
71- Disposer .dispose(toDispose)
72- }
73- }
74-
75- private fun init () {
7693 if (! JBCefApp .isSupported()) {
7794 // Fallback to an alternative browser-less solution
7895 if (AppMode .isRemoteDevHost()) {
@@ -82,11 +99,10 @@ class AmazonQPanel(private val parent: Disposable, val project: Project) {
8299 }
83100 browser.complete(null )
84101 } else {
85- val loadingPanel = JBLoadingPanel (null , parent, 0 )
102+ val loadingPanel = JBLoadingPanel (null , this )
86103 val wrapper = Wrapper ()
87104 loadingPanel.startLoading()
88105
89- loadingPanel.add(JBPanelWithEmptyText ().withEmptyText(" Wait for chat to be ready" ))
90106 webviewContainer.add(wrapper)
91107 wrapper.setContent(loadingPanel)
92108
@@ -95,12 +111,93 @@ class AmazonQPanel(private val parent: Disposable, val project: Project) {
95111 loadingPanel.stopLoading()
96112 runInEdt {
97113 browser.complete(
98- Browser (parent , webUri, project).also {
114+ Browser (this , webUri, project).also {
99115 wrapper.setContent(it.component())
116+
117+ initConnections()
118+ connectUi(it)
119+ connectApps(it)
100120 }
101121 )
102122 }
103123 }
104124 }
105125 }
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+ }
106203}
0 commit comments