Skip to content

Commit e6fe035

Browse files
committed
Merge branch 'feature/q-lsp-chat' into rli/dl-singleton
Conflicts: plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/toolwindow/AmazonQPanel.kt
2 parents 8b8b9a1 + 299daed commit e6fe035

File tree

12 files changed

+195
-235
lines changed

12 files changed

+195
-235
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"type" : "bugfix",
3+
"description" : "increase /review timeout"
4+
}

buildSrc/src/main/kotlin/software/aws/toolkits/gradle/intellij/IdeVersions.kt

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -103,40 +103,40 @@ object IdeVersions {
103103
Profile(
104104
name = "2025.1",
105105
gateway = ProductProfile(
106-
sdkVersion = "251.23774-EAP-CANDIDATE-SNAPSHOT",
106+
sdkVersion = "251.23774.441-CUSTOM-SNAPSHOT",
107107
bundledPlugins = listOf("org.jetbrains.plugins.terminal")
108108
),
109109
community = ProductProfile(
110-
sdkVersion = "251.23774.109-EAP-SNAPSHOT",
110+
sdkVersion = "2025.1",
111111
bundledPlugins = commonPlugins + listOf(
112112
"com.intellij.java",
113113
"com.intellij.gradle",
114114
"org.jetbrains.idea.maven",
115115
),
116116
marketplacePlugins = listOf(
117-
"PythonCore:251.23774.109",
118-
"Docker:251.23774.109"
117+
"PythonCore:251.23774.460",
118+
"Docker:251.23774.466"
119119
)
120120
),
121121
ultimate = ProductProfile(
122-
sdkVersion = "251.23774.109-EAP-SNAPSHOT",
122+
sdkVersion = "2025.1",
123123
bundledPlugins = commonPlugins + listOf(
124124
"JavaScript",
125125
"JavaScriptDebugger",
126126
"com.intellij.database",
127127
"com.jetbrains.codeWithMe",
128128
),
129129
marketplacePlugins = listOf(
130-
"Pythonid:251.23774.109",
131-
"org.jetbrains.plugins.go:251.23774.109",
130+
"Pythonid:251.23774.460",
131+
"org.jetbrains.plugins.go:251.23774.435",
132132
)
133133
),
134134
rider = RiderProfile(
135-
sdkVersion = "2025.1-EAP8-SNAPSHOT",
135+
sdkVersion = "2025.1",
136136
bundledPlugins = commonPlugins,
137137
netFrameworkTarget = "net472",
138138
rdGenVersion = "2025.1.1",
139-
nugetVersion = " 2025.1.0-eap08"
139+
nugetVersion = " 2025.1.0"
140140
)
141141
),
142142
).associateBy { it.name }

plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/QLoginWebview.kt

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@ import com.intellij.ui.components.panels.Wrapper
1818
import com.intellij.ui.dsl.builder.Align
1919
import com.intellij.ui.dsl.builder.panel
2020
import com.intellij.ui.jcef.JBCefJSQuery
21+
import kotlinx.coroutines.CoroutineScope
22+
import kotlinx.coroutines.flow.distinctUntilChanged
23+
import kotlinx.coroutines.flow.launchIn
24+
import kotlinx.coroutines.flow.onEach
2125
import org.cef.CefApp
2226
import software.aws.toolkits.core.utils.debug
2327
import software.aws.toolkits.core.utils.error
@@ -40,6 +44,8 @@ import software.aws.toolkits.jetbrains.services.amazonq.profile.QProfileSwitchIn
4044
import software.aws.toolkits.jetbrains.services.amazonq.profile.QRegionProfile
4145
import software.aws.toolkits.jetbrains.services.amazonq.profile.QRegionProfileManager
4246
import software.aws.toolkits.jetbrains.services.amazonq.util.createBrowser
47+
import software.aws.toolkits.jetbrains.services.amazonq.webview.theme.EditorThemeAdapter
48+
import software.aws.toolkits.jetbrains.services.amazonq.webview.theme.ThemeBrowserAdapter
4349
import software.aws.toolkits.jetbrains.utils.isQConnected
4450
import software.aws.toolkits.jetbrains.utils.isQExpired
4551
import software.aws.toolkits.jetbrains.utils.isQWebviewsAvailable
@@ -54,7 +60,7 @@ import javax.swing.JButton
5460
import javax.swing.JComponent
5561

5662
@Service(Service.Level.PROJECT)
57-
class QWebviewPanel private constructor(val project: Project) : Disposable {
63+
class QWebviewPanel private constructor(val project: Project, private val scope: CoroutineScope) : Disposable {
5864
private val webviewContainer = Wrapper()
5965
var browser: QWebviewBrowser? = null
6066
private set
@@ -102,6 +108,14 @@ class QWebviewPanel private constructor(val project: Project) : Disposable {
102108
} else {
103109
browser = QWebviewBrowser(project, this).also {
104110
webviewContainer.add(it.component())
111+
112+
val themeBrowserAdapter = ThemeBrowserAdapter()
113+
EditorThemeAdapter().onThemeChange()
114+
.distinctUntilChanged()
115+
.onEach { theme ->
116+
themeBrowserAdapter.updateLoginThemeInBrowser(it.jcefBrowser.cefBrowser, theme)
117+
}
118+
.launchIn(scope)
105119
}
106120
}
107121
}

plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/toolwindow/AmazonQPanel.kt

Lines changed: 126 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import com.intellij.openapi.components.service
1111
import com.intellij.openapi.project.Project
1212
import com.intellij.openapi.util.Disposer
1313
import com.intellij.ui.components.JBLoadingPanel
14-
import com.intellij.ui.components.JBPanelWithEmptyText
1514
import com.intellij.ui.components.JBTextArea
1615
import com.intellij.ui.components.panels.Wrapper
1716
import com.intellij.ui.dsl.builder.Align
@@ -20,17 +19,50 @@ import com.intellij.ui.dsl.builder.AlignY
2019
import com.intellij.ui.dsl.builder.panel
2120
import com.intellij.ui.jcef.JBCefApp
2221
import kotlinx.coroutines.runBlocking
22+
import kotlinx.coroutines.CoroutineScope
23+
import kotlinx.coroutines.launch
2324
import 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
2528
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
2634
import 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
2843
import java.util.concurrent.CompletableFuture
2944
import 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

Comments
 (0)