Skip to content

Commit 87be71a

Browse files
authored
Merge branch 'feature/q-lsp-chat' into q-lsp-chat
2 parents 93a9f56 + 8e9c856 commit 87be71a

File tree

28 files changed

+778
-336
lines changed

28 files changed

+778
-336
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: 130 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -7,27 +7,62 @@ import com.intellij.idea.AppMode
77
import com.intellij.openapi.Disposable
88
import com.intellij.openapi.application.ApplicationManager
99
import com.intellij.openapi.application.runInEdt
10+
import com.intellij.openapi.components.service
1011
import com.intellij.openapi.project.Project
1112
import com.intellij.openapi.util.Disposer
1213
import com.intellij.ui.components.JBLoadingPanel
13-
import com.intellij.ui.components.JBPanelWithEmptyText
1414
import com.intellij.ui.components.JBTextArea
1515
import com.intellij.ui.components.panels.Wrapper
1616
import com.intellij.ui.dsl.builder.Align
1717
import com.intellij.ui.dsl.builder.AlignX
1818
import com.intellij.ui.dsl.builder.AlignY
1919
import com.intellij.ui.dsl.builder.panel
2020
import com.intellij.ui.jcef.JBCefApp
21+
import kotlinx.coroutines.CoroutineScope
22+
import kotlinx.coroutines.launch
23+
import kotlinx.coroutines.runBlocking
2124
import 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
2334
import 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
2543
import java.util.concurrent.CompletableFuture
2644
import 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

Comments
 (0)