@@ -32,6 +32,7 @@ import com.intellij.openapi.ui.DialogPanel
32
32
import com.intellij.openapi.ui.NullableComponent
33
33
import com.intellij.openapi.ui.SimpleToolWindowPanel
34
34
import com.intellij.openapi.wm.IdeFocusManager
35
+ import com.intellij.ui.JBColor
35
36
import com.intellij.ui.JBColor.PanelBackground
36
37
import com.intellij.ui.components.JBScrollPane
37
38
import com.intellij.ui.components.panels.VerticalLayout
@@ -44,6 +45,7 @@ import kotlinx.coroutines.flow.Flow
44
45
import kotlinx.coroutines.flow.buffer
45
46
import kotlinx.coroutines.runBlocking
46
47
import kotlinx.coroutines.withContext
48
+ import java.awt.*
47
49
import java.awt.event.MouseAdapter
48
50
import java.awt.event.MouseEvent
49
51
import javax.swing.*
@@ -80,6 +82,15 @@ class NormalChatCodingPanel(private val chatCodingService: ChatCodingService, va
80
82
private var panelContent: DialogPanel
81
83
private val myScrollPane: JBScrollPane
82
84
private val delaySeconds: String get() = AutoDevSettingsState .getInstance().delaySeconds
85
+
86
+ // Add a field to hold the loading panel
87
+ private var loadingPanel: JPanel ? = null
88
+
89
+ // Add a field to track if loading animation timer is running
90
+ private var loadingTimer: Timer ? = null
91
+
92
+ // Add a field to track loading animation step
93
+ private var loadingStep = 0
83
94
84
95
init {
85
96
focusMouseListener = object : MouseAdapter () {
@@ -176,6 +187,8 @@ class NormalChatCodingPanel(private val chatCodingService: ChatCodingService, va
176
187
* Add a message to the chat panel and update ui
177
188
*/
178
189
fun addMessage (message : String , isMe : Boolean = false, displayPrompt : String = ""): MessageView {
190
+ clearLoadingView()
191
+
179
192
val role = if (isMe) ChatRole .User else ChatRole .Assistant
180
193
val displayText = displayPrompt.ifEmpty { message }
181
194
@@ -190,8 +203,101 @@ class NormalChatCodingPanel(private val chatCodingService: ChatCodingService, va
190
203
return messageView
191
204
}
192
205
193
- fun showLoading () {
194
-
206
+ fun showInitLoading () {
207
+ clearLoadingView()
208
+ loadingPanel = JPanel (BorderLayout ())
209
+ loadingPanel!! .background = UIUtil .getListBackground()
210
+ loadingPanel!! .border = JBUI .Borders .empty(10 )
211
+
212
+ val spinnerPanel = JPanel ()
213
+ spinnerPanel.isOpaque = false
214
+
215
+ val spinner = object : JPanel () {
216
+ override fun paintComponent (g : Graphics ) {
217
+ super .paintComponent(g)
218
+
219
+ val g2d = g as Graphics2D
220
+ g2d.setRenderingHint(RenderingHints .KEY_ANTIALIASING , RenderingHints .VALUE_ANTIALIAS_ON )
221
+
222
+ val width = width.toFloat()
223
+ val height = height.toFloat()
224
+ val centerX = width / 2
225
+ val centerY = height / 2
226
+ val radius = minOf(width, height) / 2 - 5
227
+
228
+ val oldStroke = g2d.stroke
229
+ g2d.stroke = BasicStroke (3f )
230
+
231
+ val step = if (loadingStep >= 12 ) 0 else loadingStep
232
+ for (i in 0 until 12 ) {
233
+ g2d.color = JBColor (
234
+ Color (47 , 99 , 162 , 255 - ((i + 12 - step) % 12 ) * 20 ),
235
+ Color (88 , 157 , 246 , 255 - ((i + 12 - step) % 12 ) * 20 )
236
+ )
237
+
238
+ val startAngle = i * 30
239
+ g2d.drawArc(
240
+ (centerX - radius).toInt(),
241
+ (centerY - radius).toInt(),
242
+ (radius * 2 ).toInt(),
243
+ (radius * 2 ).toInt(),
244
+ startAngle,
245
+ 15
246
+ )
247
+ }
248
+
249
+ g2d.stroke = oldStroke
250
+ }
251
+
252
+ override fun getPreferredSize (): Dimension {
253
+ return Dimension (40 , 40 )
254
+ }
255
+ }
256
+
257
+ spinnerPanel.add(spinner)
258
+
259
+ val loadingLabel = JLabel (" Loading" , SwingConstants .CENTER )
260
+ loadingPanel!! .add(spinnerPanel, BorderLayout .CENTER )
261
+ loadingPanel!! .add(loadingLabel, BorderLayout .SOUTH )
262
+
263
+ runInEdt {
264
+ myList.add(loadingPanel!! )
265
+ updateUI()
266
+ scrollToBottom()
267
+ }
268
+
269
+ loadingStep = 0
270
+
271
+ loadingTimer = Timer (100 , null ) // 100ms interval for smooth animation
272
+ loadingTimer!! .addActionListener {
273
+ loadingStep = (loadingStep + 1 ) % 12
274
+ spinner.repaint()
275
+ }
276
+
277
+ loadingTimer!! .start()
278
+ }
279
+
280
+ private fun clearLoadingView () {
281
+ // Stop the timer first
282
+ loadingTimer?.stop()
283
+ loadingTimer = null
284
+
285
+ // Safely remove the loading panel if it exists
286
+ if (loadingPanel != null ) {
287
+ val panelToRemove = loadingPanel
288
+ runInEdt {
289
+ // Check if the panel is still in the component hierarchy
290
+ if (panelToRemove != null && panelToRemove.parent == = myList) {
291
+ try {
292
+ myList.remove(panelToRemove)
293
+ updateUI()
294
+ } catch (e: Exception ) {
295
+ // Log or handle any exceptions that might occur during removal
296
+ }
297
+ }
298
+ }
299
+ loadingPanel = null
300
+ }
195
301
}
196
302
197
303
fun getHistoryMessages (): List <LlmMsg .ChatMessage > {
@@ -297,6 +403,7 @@ class NormalChatCodingPanel(private val chatCodingService: ChatCodingService, va
297
403
override fun resetChatSession () {
298
404
chatCodingService.stop()
299
405
chatCodingService.clearSession()
406
+ clearLoadingView() // Clear loading view when resetting chat
300
407
myList.removeAll()
301
408
this .hiddenProgressBar()
302
409
this .resetAgent()
0 commit comments