@@ -14,18 +14,18 @@ import com.intellij.openapi.actionSystem.AnActionEvent
14
14
import com.intellij.openapi.project.Project
15
15
import com.intellij.openapi.ui.Messages
16
16
import com.intellij.openapi.ui.popup.JBPopupFactory
17
+ import com.intellij.openapi.ui.popup.JBPopup
17
18
import com.intellij.openapi.wm.ToolWindowManager
18
- import com.intellij.ui.ColoredListCellRenderer
19
- import com.intellij.ui.SimpleTextAttributes
20
19
import com.intellij.ui.components.JBList
20
+ import com.intellij.ui.components.JBScrollPane
21
21
import com.intellij.util.ui.JBUI
22
- import java.awt.BorderLayout
22
+ import java.awt.*
23
+ import java.awt.event.ComponentAdapter
24
+ import java.awt.event.ComponentEvent
23
25
import java.awt.event.MouseAdapter
24
26
import java.awt.event.MouseEvent
25
- import javax.swing.JButton
26
- import javax.swing.JLabel
27
- import javax.swing.JPanel
28
- import javax.swing.ListSelectionModel
27
+ import javax.swing.*
28
+ import javax.swing.border.EmptyBorder
29
29
30
30
class ViewHistoryAction : AnAction (
31
31
AutoDevBundle .message("action.view.history.text"),
@@ -37,15 +37,15 @@ class ViewHistoryAction : AnAction(
37
37
private fun formatRelativeTime (timestamp : Long ): String {
38
38
val now = System .currentTimeMillis()
39
39
val diff = now - timestamp
40
-
40
+
41
41
val seconds = diff / 1000
42
42
val minutes = seconds / 60
43
43
val hours = minutes / 60
44
44
val days = hours / 24
45
45
val weeks = days / 7
46
46
val months = days / 30
47
47
val years = days / 365
48
-
48
+
49
49
return when {
50
50
years > 0 -> " ${years} 年前"
51
51
months > 0 -> " ${months} 个月前"
@@ -65,51 +65,95 @@ class ViewHistoryAction : AnAction(
65
65
private inner class SessionListCellRenderer (
66
66
private val project : Project ,
67
67
private val onDelete : (ChatSessionHistory ) -> Unit
68
- ) : ColoredListCellRenderer <SessionListItem>() {
68
+ ) : ListCellRenderer <SessionListItem> {
69
69
70
- override fun customizeCellRenderer (
70
+ override fun getListCellRendererComponent (
71
71
list : javax.swing.JList <out SessionListItem >,
72
- value : SessionListItem ? ,
72
+ value : SessionListItem ,
73
73
index : Int ,
74
74
selected : Boolean ,
75
75
hasFocus : Boolean
76
- ) {
77
- if (value == null ) return
78
-
79
- // 创建主面板
76
+ ): Component {
77
+ // 创建主面板,使用 BorderLayout
80
78
val panel = JPanel (BorderLayout ())
81
- panel.border = JBUI .Borders .empty(4 , 8 )
82
-
83
- // 左侧标题
84
- val titleLabel = JLabel (value.session.name)
85
- titleLabel.font = titleLabel.font.deriveFont(14f )
86
-
87
- // 右侧时间和删除按钮的面板
88
- val rightPanel = JPanel (BorderLayout ())
79
+ panel.border = JBUI .Borders .empty(8 , 12 )
80
+
81
+ // 设置背景颜色
82
+ if (selected) {
83
+ panel.background = list.selectionBackground
84
+ panel.foreground = list.selectionForeground
85
+ } else {
86
+ panel.background = list.background
87
+ panel.foreground = list.foreground
88
+ }
89
+ panel.isOpaque = true
90
+
91
+ // 左侧内容面板 - 包含会话名称和时间
92
+ val contentPanel = JPanel ()
93
+ contentPanel.layout = BoxLayout (contentPanel, BoxLayout .Y_AXIS )
94
+ contentPanel.isOpaque = false
95
+
96
+ // 会话名称标签 - 处理过长文本
97
+ val sessionName = value.session.name
98
+ val displayName = if (sessionName.length > 40 ) {
99
+ sessionName.substring(0 , 37 ) + " ..."
100
+ } else {
101
+ sessionName
102
+ }
89
103
104
+ val titleLabel = JLabel (displayName)
105
+ titleLabel.font = titleLabel.font.deriveFont(Font .BOLD , 13f )
106
+ titleLabel.toolTipText = sessionName // 完整名称作为工具提示
107
+ if (selected) {
108
+ titleLabel.foreground = list.selectionForeground
109
+ } else {
110
+ titleLabel.foreground = list.foreground
111
+ }
112
+
90
113
// 时间标签
91
114
val timeLabel = JLabel (value.relativeTime)
92
- timeLabel.font = timeLabel.font.deriveFont(12f )
93
- timeLabel.foreground = if (selected) list.selectionForeground else list.foreground.darker()
94
-
95
- // 删除按钮
96
- val deleteButton = JButton (AllIcons .Actions .Close )
97
- deleteButton.isOpaque = false
98
- deleteButton.isBorderPainted = false
99
- deleteButton.preferredSize = JBUI .size(16 , 16 )
100
- deleteButton.addActionListener {
101
- onDelete(value.session)
115
+ timeLabel.font = timeLabel.font.deriveFont(11f )
116
+ if (selected) {
117
+ timeLabel.foreground = list.selectionForeground.darker()
118
+ } else {
119
+ timeLabel.foreground = list.foreground.darker()
102
120
}
103
-
104
- rightPanel.add(timeLabel, BorderLayout .CENTER )
105
- rightPanel.add(deleteButton, BorderLayout .EAST )
106
-
107
- panel.add(titleLabel, BorderLayout .WEST )
108
- panel.add(rightPanel, BorderLayout .EAST )
109
-
110
- // 设置简单的文本渲染
111
- append(value.session.name, SimpleTextAttributes .REGULAR_ATTRIBUTES )
112
- append(" - ${value.relativeTime} " , SimpleTextAttributes .GRAYED_ATTRIBUTES )
121
+
122
+ // 设置标签对齐
123
+ titleLabel.alignmentX = Component .LEFT_ALIGNMENT
124
+ timeLabel.alignmentX = Component .LEFT_ALIGNMENT
125
+
126
+ contentPanel.add(titleLabel)
127
+ contentPanel.add(Box .createVerticalStrut(2 ))
128
+ contentPanel.add(timeLabel)
129
+
130
+ // 右侧删除按钮
131
+ val deleteButton = JLabel (AllIcons .Actions .Close )
132
+ deleteButton.cursor = Cursor .getPredefinedCursor(Cursor .HAND_CURSOR )
133
+ deleteButton.border = JBUI .Borders .emptyLeft(8 )
134
+ deleteButton.addMouseListener(object : MouseAdapter () {
135
+ override fun mouseClicked (e : MouseEvent ) {
136
+ e.consume() // 防止事件传播到列表
137
+ onDelete(value.session)
138
+ }
139
+
140
+ override fun mouseEntered (e : MouseEvent ) {
141
+ deleteButton.icon = AllIcons .Actions .CloseHovered
142
+ }
143
+
144
+ override fun mouseExited (e : MouseEvent ) {
145
+ deleteButton.icon = AllIcons .Actions .Close
146
+ }
147
+ })
148
+
149
+ // 组装面板
150
+ panel.add(contentPanel, BorderLayout .CENTER )
151
+ panel.add(deleteButton, BorderLayout .EAST )
152
+
153
+ // 设置固定高度
154
+ panel.preferredSize = Dimension (panel.preferredSize.width, 50 )
155
+
156
+ return panel
113
157
}
114
158
}
115
159
@@ -126,26 +170,36 @@ class ViewHistoryAction : AnAction(
126
170
val listItems = sessions.map { SessionListItem (it, formatRelativeTime(it.createdAt)) }
127
171
val jbList = JBList (listItems)
128
172
jbList.selectionMode = ListSelectionModel .SINGLE_SELECTION
129
-
173
+ jbList.fixedCellHeight = 50 // 固定单元格高度
174
+
130
175
val onDeleteSession = { session: ChatSessionHistory ->
131
176
val result = Messages .showYesNoDialog(
132
177
project,
133
178
" 确定要删除会话 \" ${session.name} \" 吗?" ,
134
179
" 删除会话" ,
135
180
Messages .getQuestionIcon()
136
181
)
137
-
182
+
138
183
if (result == Messages .YES ) {
139
184
historyService.deleteSession(session.id)
140
185
sessions = historyService.getAllSessions().sortedByDescending { it.createdAt }
141
- // 关闭当前popup并重新创建
142
186
createAndShowPopup()
143
187
}
144
188
}
145
-
189
+
146
190
jbList.cellRenderer = SessionListCellRenderer (project, onDeleteSession)
191
+
192
+ val scrollPane = JBScrollPane (jbList)
193
+ scrollPane.border = null
194
+
195
+ // 设置 Popup 的固定宽度和自适应高度
196
+ val popupWidth = 450
197
+ val maxPopupHeight = 400
198
+ val itemHeight = 50
199
+ val calculatedHeight = (sessions.size * itemHeight + 20 ).coerceAtMost(maxPopupHeight)
147
200
148
- // 添加右键菜单支持
201
+ scrollPane.preferredSize = Dimension (popupWidth, calculatedHeight)
202
+
149
203
jbList.addMouseListener(object : MouseAdapter () {
150
204
override fun mouseClicked (e : MouseEvent ) {
151
205
if (e.button == MouseEvent .BUTTON3 ) { // 右键
@@ -159,23 +213,32 @@ class ViewHistoryAction : AnAction(
159
213
}
160
214
})
161
215
162
- JBPopupFactory .getInstance()
163
- .createListPopupBuilder( jbList)
216
+ val popupBuilder = JBPopupFactory .getInstance()
217
+ .createComponentPopupBuilder(scrollPane, jbList)
164
218
.setTitle(AutoDevBundle .message(" popup.title.session.history" ))
165
219
.setMovable(true )
166
220
.setResizable(true )
167
221
.setRequestFocus(true )
168
- .setItemChoosenCallback {
222
+ .setCancelOnClickOutside(true )
223
+ .setCancelOnOtherWindowOpen(true )
224
+
225
+ val popup: JBPopup = popupBuilder.createPopup()
226
+
227
+ // 设置 Popup 的最小和最大尺寸
228
+ popup.setMinimumSize(Dimension (popupWidth, 150 ))
229
+
230
+ jbList.addListSelectionListener {
231
+ if (! it.valueIsAdjusting && jbList.selectedIndex != - 1 ) {
169
232
val selectedIndex = jbList.selectedIndex
170
- if (selectedIndex != - 1 ) {
171
- val selectedSession = listItems[selectedIndex].session
172
- loadSessionIntoSketch(project, selectedSession)
173
- }
233
+ popup.closeOk(null )
234
+ val selectedSession = listItems[selectedIndex].session
235
+ loadSessionIntoSketch(project, selectedSession)
174
236
}
175
- .createPopup()
176
- .showInBestPositionFor(e.dataContext)
237
+ }
238
+
239
+ popup.showInBestPositionFor(e.dataContext)
177
240
}
178
-
241
+
179
242
createAndShowPopup()
180
243
}
181
244
@@ -196,7 +259,7 @@ class ViewHistoryAction : AnAction(
196
259
197
260
it.displayMessages(session.messages)
198
261
session.messages.firstOrNull { msg -> msg.role == " user" }?.content?.let { intention ->
199
- agentStateService.state = agentStateService.state.copy(originIntention = intention)
262
+ agentStateService.state = agentStateService.state.copy(originIntention = intention)
200
263
}
201
264
202
265
toolWindow.activate(null )
0 commit comments