@@ -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,11 @@ 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" ))
104+ // loadingPanel.add(JBPanelWithEmptyText().withEmptyText("Wait for chat to be ready"))
87105 webviewContainer.add(wrapper)
88106 wrapper.setContent(loadingPanel)
89107
@@ -92,12 +110,93 @@ class AmazonQPanel(private val parent: Disposable, val project: Project) {
92110 loadingPanel.stopLoading()
93111 runInEdt {
94112 browser.complete(
95- Browser (parent , webUri, project).also {
113+ Browser (this , webUri, project).also {
96114 wrapper.setContent(it.component())
115+
116+ initConnections()
117+ connectUi(it)
118+ connectApps(it)
97119 }
98120 )
99121 }
100122 }
101123 }
102124 }
125+
126+ fun sendMessage (message : AmazonQMessage , tabType : String ) {
127+ appConnections.filter { it.app.tabTypes.contains(tabType) }.forEach {
128+ scope.launch {
129+ it.messagesFromUiToApp.publish(message)
130+ }
131+ }
132+ }
133+
134+ fun sendMessageAppToUi (message : AmazonQMessage , tabType : String ) {
135+ appConnections.filter { it.app.tabTypes.contains(tabType) }.forEach {
136+ scope.launch {
137+ it.messagesFromAppToUi.publish(message)
138+ }
139+ }
140+ }
141+
142+ private fun initConnections () {
143+ val apps = appSource.getApps(project)
144+ apps.forEach { app ->
145+ appConnections + = AppConnection (
146+ app = app,
147+ messagesFromAppToUi = MessageConnector (),
148+ messagesFromUiToApp = MessageConnector (),
149+ messageTypeRegistry = MessageTypeRegistry (),
150+ )
151+ }
152+ }
153+
154+ private fun connectApps (browser : Browser ) {
155+ val fqnWebviewAdapter = FqnWebviewAdapter (browser.jcefBrowser, browserConnector)
156+
157+ appConnections.forEach { connection ->
158+ val initContext = AmazonQAppInitContext (
159+ project = project,
160+ messagesFromAppToUi = connection.messagesFromAppToUi,
161+ messagesFromUiToApp = connection.messagesFromUiToApp,
162+ messageTypeRegistry = connection.messageTypeRegistry,
163+ fqnWebviewAdapter = fqnWebviewAdapter,
164+ )
165+ // Connect the app to the UI
166+ connection.app.init (initContext)
167+ // Dispose of the app when the tool window is disposed.
168+ Disposer .register(this , connection.app)
169+ }
170+ }
171+
172+ private fun connectUi (browser : Browser ) {
173+ browser.init (
174+ isCodeTransformAvailable = isCodeTransformAvailable(project),
175+ isFeatureDevAvailable = isFeatureDevAvailable(project),
176+ isCodeScanAvailable = isCodeScanAvailable(project),
177+ isCodeTestAvailable = isCodeTestAvailable(project),
178+ isDocAvailable = isDocAvailable(project),
179+ highlightCommand = highlightCommand(),
180+ activeProfile = QRegionProfileManager .getInstance().takeIf { it.shouldDisplayProfileInfo(project) }?.activeProfile(project)
181+ )
182+
183+ scope.launch {
184+ // Pipe messages from the UI to the relevant apps and vice versa
185+ browserConnector.connect(
186+ browser = browser,
187+ connections = appConnections,
188+ )
189+ }
190+
191+ scope.launch {
192+ // Update the theme in the UI when the IDE theme changes
193+ browserConnector.connectTheme(
194+ chatBrowser = browser.jcefBrowser.cefBrowser,
195+ themeSource = editorThemeAdapter.onThemeChange(),
196+ )
197+ }
198+ }
199+
200+ override fun dispose () {
201+ }
103202}
0 commit comments