Skip to content

Commit 12cd69e

Browse files
committed
feat(gui): enhance session history popup with relative time display and delete functionality
1 parent 04f540b commit 12cd69e

File tree

1 file changed

+170
-24
lines changed

1 file changed

+170
-24
lines changed

core/src/main/kotlin/cc/unitmesh/devti/gui/toolbar/ViewHistoryAction.kt

Lines changed: 170 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,23 @@ import com.intellij.openapi.actionSystem.ActionUpdateThread
1212
import com.intellij.openapi.actionSystem.AnAction
1313
import com.intellij.openapi.actionSystem.AnActionEvent
1414
import com.intellij.openapi.project.Project
15+
import com.intellij.openapi.ui.Messages
1516
import com.intellij.openapi.ui.popup.JBPopupFactory
17+
import com.intellij.openapi.ui.popup.PopupStep
18+
import com.intellij.openapi.ui.popup.util.BaseListPopupStep
1619
import com.intellij.openapi.wm.ToolWindowManager
20+
import com.intellij.ui.ColoredListCellRenderer
21+
import com.intellij.ui.SimpleTextAttributes
1722
import com.intellij.ui.components.JBList
18-
import java.text.SimpleDateFormat
23+
import com.intellij.util.ui.JBUI
1924
import java.util.*
25+
import java.awt.BorderLayout
26+
import java.awt.Component
27+
import java.awt.event.MouseAdapter
28+
import java.awt.event.MouseEvent
29+
import javax.swing.JButton
30+
import javax.swing.JPanel
31+
import javax.swing.JLabel
2032
import javax.swing.ListSelectionModel
2133

2234
class ViewHistoryAction : AnAction(
@@ -26,39 +38,173 @@ class ViewHistoryAction : AnAction(
2638
) {
2739
override fun getActionUpdateThread(): ActionUpdateThread = ActionUpdateThread.BGT
2840

41+
private fun formatRelativeTime(timestamp: Long): String {
42+
val now = System.currentTimeMillis()
43+
val diff = now - timestamp
44+
45+
val seconds = diff / 1000
46+
val minutes = seconds / 60
47+
val hours = minutes / 60
48+
val days = hours / 24
49+
val weeks = days / 7
50+
val months = days / 30
51+
val years = days / 365
52+
53+
return when {
54+
years > 0 -> "${years}年前"
55+
months > 0 -> "${months}个月前"
56+
weeks > 0 -> "${weeks}周前"
57+
days > 0 -> "${days}天前"
58+
hours > 0 -> "${hours}小时前"
59+
minutes > 0 -> "${minutes}分钟前"
60+
else -> "刚刚"
61+
}
62+
}
63+
64+
private inner class SessionListItem(
65+
val session: ChatSessionHistory,
66+
val relativeTime: String
67+
)
68+
69+
private inner class SessionListCellRenderer(
70+
private val project: Project,
71+
private val onDelete: (ChatSessionHistory) -> Unit
72+
) : ColoredListCellRenderer<SessionListItem>() {
73+
74+
override fun customizeCellRenderer(
75+
list: javax.swing.JList<out SessionListItem>,
76+
value: SessionListItem?,
77+
index: Int,
78+
selected: Boolean,
79+
hasFocus: Boolean
80+
) {
81+
if (value == null) return
82+
83+
// 创建主面板
84+
val panel = JPanel(BorderLayout())
85+
panel.border = JBUI.Borders.empty(4, 8)
86+
87+
// 左侧标题
88+
val titleLabel = JLabel(value.session.name)
89+
titleLabel.font = titleLabel.font.deriveFont(14f)
90+
91+
// 右侧时间和删除按钮的面板
92+
val rightPanel = JPanel(BorderLayout())
93+
94+
// 时间标签
95+
val timeLabel = JLabel(value.relativeTime)
96+
timeLabel.font = timeLabel.font.deriveFont(12f)
97+
timeLabel.foreground = if (selected) list.selectionForeground else list.foreground.darker()
98+
99+
// 删除按钮
100+
val deleteButton = JButton(AllIcons.Actions.Close)
101+
deleteButton.isOpaque = false
102+
deleteButton.isBorderPainted = false
103+
deleteButton.preferredSize = JBUI.size(16, 16)
104+
deleteButton.addActionListener {
105+
onDelete(value.session)
106+
}
107+
108+
rightPanel.add(timeLabel, BorderLayout.CENTER)
109+
rightPanel.add(deleteButton, BorderLayout.EAST)
110+
111+
panel.add(titleLabel, BorderLayout.WEST)
112+
panel.add(rightPanel, BorderLayout.EAST)
113+
114+
// 设置简单的文本渲染
115+
append(value.session.name, SimpleTextAttributes.REGULAR_ATTRIBUTES)
116+
append(" - ${value.relativeTime}", SimpleTextAttributes.GRAYED_ATTRIBUTES)
117+
}
118+
}
119+
120+
private inner class SessionPopupStep(
121+
private val project: Project,
122+
private val sessions: List<ChatSessionHistory>
123+
) : BaseListPopupStep<SessionListItem>(
124+
AutoDevBundle.message("popup.title.session.history"),
125+
sessions.map { SessionListItem(it, formatRelativeTime(it.createdAt)) }
126+
) {
127+
128+
override fun getTextFor(value: SessionListItem): String {
129+
return value.session.name
130+
}
131+
132+
override fun onChosen(selectedValue: SessionListItem, finalChoice: Boolean): PopupStep<*>? {
133+
if (finalChoice) {
134+
loadSessionIntoSketch(project, selectedValue.session)
135+
}
136+
return PopupStep.FINAL_CHOICE
137+
}
138+
139+
override fun hasSubstep(selectedValue: SessionListItem): Boolean = false
140+
141+
override fun isSelectable(value: SessionListItem): Boolean = true
142+
}
143+
29144
override fun actionPerformed(e: AnActionEvent) {
30145
val project = e.project ?: return
31146
val historyService = project.getService(ChatHistoryService::class.java)
32-
val sessions = historyService.getAllSessions().sortedByDescending { it.createdAt }
147+
var sessions = historyService.getAllSessions().sortedByDescending { it.createdAt }
33148

34149
if (sessions.isEmpty()) {
35-
// Optionally show a message if no history is available
36150
return
37151
}
38152

39-
val listModel = sessions.map {
40-
val date = SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(Date(it.createdAt))
41-
"${it.name} - $date"
42-
}
43-
44-
val jbList = JBList(listModel)
45-
jbList.selectionMode = ListSelectionModel.SINGLE_SELECTION
46-
47-
JBPopupFactory.getInstance()
48-
.createListPopupBuilder(jbList)
49-
.setTitle(AutoDevBundle.message("popup.title.session.history"))
50-
.setMovable(true)
51-
.setResizable(true)
52-
.setRequestFocus(true)
53-
.setItemChoosenCallback {
54-
val selectedIndex = jbList.selectedIndex
55-
if (selectedIndex != -1) {
56-
val selectedSession = sessions[selectedIndex]
57-
loadSessionIntoSketch(project, selectedSession)
153+
fun createAndShowPopup() {
154+
val listItems = sessions.map { SessionListItem(it, formatRelativeTime(it.createdAt)) }
155+
val jbList = JBList(listItems)
156+
jbList.selectionMode = ListSelectionModel.SINGLE_SELECTION
157+
158+
val onDeleteSession = { session: ChatSessionHistory ->
159+
val result = Messages.showYesNoDialog(
160+
project,
161+
"确定要删除会话 \"${session.name}\" 吗?",
162+
"删除会话",
163+
Messages.getQuestionIcon()
164+
)
165+
166+
if (result == Messages.YES) {
167+
historyService.deleteSession(session.id)
168+
sessions = historyService.getAllSessions().sortedByDescending { it.createdAt }
169+
// 关闭当前popup并重新创建
170+
createAndShowPopup()
58171
}
59172
}
60-
.createPopup()
61-
.showInBestPositionFor(e.dataContext)
173+
174+
jbList.cellRenderer = SessionListCellRenderer(project, onDeleteSession)
175+
176+
// 添加右键菜单支持
177+
jbList.addMouseListener(object : MouseAdapter() {
178+
override fun mouseClicked(e: MouseEvent) {
179+
if (e.button == MouseEvent.BUTTON3) { // 右键
180+
val index = jbList.locationToIndex(e.point)
181+
if (index >= 0) {
182+
jbList.selectedIndex = index
183+
val selectedSession = listItems[index].session
184+
onDeleteSession(selectedSession)
185+
}
186+
}
187+
}
188+
})
189+
190+
JBPopupFactory.getInstance()
191+
.createListPopupBuilder(jbList)
192+
.setTitle(AutoDevBundle.message("popup.title.session.history"))
193+
.setMovable(true)
194+
.setResizable(true)
195+
.setRequestFocus(true)
196+
.setItemChoosenCallback {
197+
val selectedIndex = jbList.selectedIndex
198+
if (selectedIndex != -1) {
199+
val selectedSession = listItems[selectedIndex].session
200+
loadSessionIntoSketch(project, selectedSession)
201+
}
202+
}
203+
.createPopup()
204+
.showInBestPositionFor(e.dataContext)
205+
}
206+
207+
createAndShowPopup()
62208
}
63209

64210
private fun loadSessionIntoSketch(project: Project, session: ChatSessionHistory) {

0 commit comments

Comments
 (0)