1
1
package cc.unitmesh.devti.sketch.ui.plan
2
2
3
- import cc.unitmesh.devti.AutoDevBundle
4
- import cc.unitmesh.devti.gui.AutoDevPlanerToolWindowFactory
5
- import cc.unitmesh.devti.gui.AutoDevToolWindowFactory
6
- import cc.unitmesh.devti.gui.chat.message.ChatActionType
7
3
import cc.unitmesh.devti.observer.agent.AgentStateService
8
- import cc.unitmesh.devti.observer.plan.AgentPlanStep
9
4
import cc.unitmesh.devti.observer.plan.AgentTaskEntry
10
5
import cc.unitmesh.devti.observer.plan.MarkdownPlanParser
11
6
import cc.unitmesh.devti.observer.plan.TaskStatus
12
7
import cc.unitmesh.devti.sketch.ui.ExtensionLangSketch
13
- import com.intellij.icons.AllIcons
14
8
import com.intellij.lang.Language
15
- import com.intellij.openapi.actionSystem.ActionManager
16
- import com.intellij.openapi.actionSystem.AnAction
17
- import com.intellij.openapi.actionSystem.AnActionEvent
18
- import com.intellij.openapi.actionSystem.DefaultActionGroup
19
9
import com.intellij.openapi.project.Project
20
- import com.intellij.openapi.wm.ToolWindowManager
21
- import com.intellij.ui.components.JBCheckBox
22
10
import com.intellij.ui.components.JBPanel
23
- import com.intellij.ui.components.panels.Wrapper
24
11
import com.intellij.util.ui.JBEmptyBorder
25
12
import com.intellij.util.ui.JBUI
26
- import com.intellij.util.ui.UIUtil
27
13
import java.awt.BorderLayout
28
- import java.awt.Dimension
29
- import java.awt.FlowLayout
30
- import javax.swing.BorderFactory
31
14
import javax.swing.Box
32
15
import javax.swing.BoxLayout
33
- import javax.swing.JButton
34
16
import javax.swing.JComponent
35
- import javax.swing.JLabel
36
- import javax.swing.JMenuItem
37
17
import javax.swing.JPanel
38
- import javax.swing.JPopupMenu
39
-
40
- /* *
41
- * Task Panel UI Component responsible for rendering and handling interactions for a single task
42
- */
43
- class TaskPanel (
44
- private val project : Project ,
45
- private val task : AgentPlanStep ,
46
- private val onStatusChange : () -> Unit
47
- ) : JBPanel<JBPanel<*>>(FlowLayout (FlowLayout .LEFT , 2, 0)) {
48
-
49
- private val taskLabel: JLabel
50
-
51
- init {
52
- border = JBUI .Borders .empty(1 , 16 , 1 , 0 )
53
- taskLabel = createStyledTaskLabel()
54
-
55
- val statusIcon = createStatusIcon()
56
- add(statusIcon)
57
-
58
- if (task.status == TaskStatus .TODO ) {
59
- add(createExecuteButton())
60
- }
61
-
62
- add(taskLabel)
63
- setupContextMenu()
64
- }
65
-
66
- private fun createStatusIcon (): JComponent {
67
- return when (task.status) {
68
- TaskStatus .COMPLETED -> JLabel (AllIcons .Actions .Checked )
69
- TaskStatus .FAILED -> JLabel (AllIcons .General .Error )
70
- TaskStatus .IN_PROGRESS -> JLabel (AllIcons .Toolwindows .ToolWindowBuild )
71
- TaskStatus .TODO -> JBCheckBox ().apply {
72
- isSelected = task.completed
73
- addActionListener {
74
- task.completed = isSelected
75
- task.updateStatus(if (isSelected) TaskStatus .COMPLETED else TaskStatus .TODO )
76
- updateTaskLabel()
77
- onStatusChange()
78
- }
79
- isBorderPainted = false
80
- isContentAreaFilled = false
81
- }
82
- }
83
- }
84
-
85
- private fun createExecuteButton (): JButton {
86
- return JButton (AllIcons .Actions .Execute ).apply {
87
- border = BorderFactory .createEmptyBorder()
88
- isOpaque = true
89
- preferredSize = Dimension (20 , 20 )
90
- toolTipText = " Execute"
91
-
92
- addActionListener {
93
- AutoDevToolWindowFactory .Companion .sendToSketchToolWindow(project, ChatActionType .SKETCH ) { ui, _ ->
94
- ui.sendInput(AutoDevBundle .message(" sketch.plan.finish.task" ) + task.step)
95
- }
96
- }
97
- }
98
- }
99
-
100
- private fun createStyledTaskLabel (): JLabel {
101
- val labelText = getLabelTextByStatus()
102
- return JLabel (labelText).apply {
103
- border = JBUI .Borders .emptyLeft(5 )
104
- }
105
- }
106
-
107
- private fun getLabelTextByStatus (): String {
108
- return when (task.status) {
109
- TaskStatus .COMPLETED -> " <html><strike>${task.step} </strike></html>"
110
- TaskStatus .FAILED -> " <html><span style='color:red'>${task.step} </span></html>"
111
- TaskStatus .IN_PROGRESS -> " <html><span style='color:blue;font-style:italic'>${task.step} </span></html>"
112
- TaskStatus .TODO -> task.step
113
- }
114
- }
115
-
116
- private fun updateTaskLabel () {
117
- taskLabel.text = getLabelTextByStatus()
118
- }
119
-
120
- private fun setupContextMenu () {
121
- val taskPopupMenu = JPopupMenu ()
122
-
123
- val statusMenuItems = mapOf (
124
- " Mark as Completed [✓]" to TaskStatus .COMPLETED ,
125
- " Mark as In Progress [*]" to TaskStatus .IN_PROGRESS ,
126
- " Mark as Failed [!]" to TaskStatus .FAILED ,
127
- " Mark as Todo [ ]" to TaskStatus .TODO
128
- )
129
-
130
- statusMenuItems.forEach { (label, status) ->
131
- val menuItem = JMenuItem (label)
132
- menuItem.addActionListener {
133
- task.updateStatus(status)
134
- updateTaskLabel()
135
- onStatusChange()
136
- }
137
- taskPopupMenu.add(menuItem)
138
- }
139
-
140
- taskLabel.componentPopupMenu = taskPopupMenu
141
- }
142
- }
143
-
144
- /* *
145
- * Section Panel UI Component responsible for rendering and handling interactions for a plan section
146
- */
147
- class SectionPanel (
148
- private val project : Project ,
149
- private val index : Int ,
150
- private val planItem : AgentTaskEntry ,
151
- private val onStatusChange : () -> Unit
152
- ) : JBPanel<JBPanel<*>>(BorderLayout ()) {
153
-
154
- init {
155
- layout = BoxLayout (this , BoxLayout .Y_AXIS )
156
- val titlePanel = createSectionTitlePanel()
157
- add(titlePanel)
158
-
159
- // Add all tasks in this section
160
- planItem.steps.forEach { task ->
161
- add(TaskPanel (project, task) {
162
- updateSectionStatus()
163
- onStatusChange()
164
- })
165
- }
166
- }
167
-
168
- private fun createSectionTitlePanel (): JPanel {
169
- val titlePanel = JBPanel <JBPanel <* >>(FlowLayout (FlowLayout .LEFT , 2 , 0 )).apply {
170
- border = JBUI .Borders .empty(2 )
171
- }
172
-
173
- // Add execute button for todo sections
174
- if (planItem.status == TaskStatus .TODO && ! planItem.completed) {
175
- titlePanel.add(createExecuteSectionButton())
176
- }
177
-
178
- val statusIndicator = when (planItem.status) {
179
- TaskStatus .COMPLETED -> " ✓"
180
- TaskStatus .FAILED -> " !"
181
- TaskStatus .IN_PROGRESS -> " *"
182
- TaskStatus .TODO -> " "
183
- }
184
-
185
- val titleText = if (statusIndicator.isNotEmpty()) {
186
- " <html><b>${index + 1 } . ${planItem.title} [$statusIndicator ]</b></html>"
187
- } else {
188
- " <html><b>${index + 1 } . ${planItem.title} </b></html>"
189
- }
190
-
191
- val sectionLabel = JLabel (titleText)
192
- sectionLabel.border = JBUI .Borders .emptyLeft(2 )
193
-
194
- titlePanel.add(sectionLabel)
195
- return titlePanel
196
- }
197
-
198
- private fun createExecuteSectionButton (): JButton {
199
- return JButton (AllIcons .Actions .Execute ).apply {
200
- border = BorderFactory .createEmptyBorder()
201
- isOpaque = true
202
- preferredSize = Dimension (20 , 20 )
203
- toolTipText = " Execute Task"
204
-
205
- addActionListener {
206
- AutoDevToolWindowFactory .Companion .sendToSketchToolWindow(project, ChatActionType .SKETCH ) { ui, _ ->
207
- val allSteps = planItem.steps.joinToString(" \n " ) { it.step }
208
- ui.sendInput(AutoDevBundle .message(" sketch.plan.finish.task" ) + allSteps)
209
- }
210
- }
211
- }
212
- }
213
-
214
- fun updateSectionStatus () {
215
- planItem.updateCompletionStatus()
216
- }
217
- }
218
-
219
- /* *
220
- * Toolbar factory for creating the plan sketch toolbar
221
- */
222
- class PlanToolbarFactory (private val project : Project ) {
223
- fun createToolbar (): JComponent {
224
- val actionGroup = DefaultActionGroup (createToolbarActions())
225
- val toolbar = ActionManager .getInstance()
226
- .createActionToolbar(" PlanSketch" , actionGroup, true )
227
-
228
- val titleLabel = JLabel (" Thought Plan" ).apply {
229
- border = JBUI .Borders .empty(0 , 10 )
230
- }
231
-
232
- val toolbarPanel = JPanel (BorderLayout ()).apply {
233
- add(titleLabel, BorderLayout .WEST )
234
- add(toolbar.component, BorderLayout .EAST )
235
- }
236
-
237
- val toolbarWrapper = Wrapper (JBUI .Panels .simplePanel(toolbarPanel)).also {
238
- it.border = JBUI .Borders .customLine(UIUtil .getBoundsColor(), 1 , 1 , 1 , 1 )
239
- }
240
-
241
- return toolbarWrapper
242
- }
243
-
244
- private fun createToolbarActions (): List <AnAction > {
245
- val pinAction = object : AnAction (" Pin" , " Show in popup window" , AllIcons .Toolbar .Pin ) {
246
- override fun displayTextInToolbar (): Boolean = true
247
-
248
- override fun actionPerformed (e : AnActionEvent ) {
249
- val toolWindow =
250
- ToolWindowManager .Companion .getInstance(project).getToolWindow(AutoDevPlanerToolWindowFactory .Companion .PlANNER_ID )
251
- ? : return
252
-
253
- toolWindow.activate {
254
- // todo
255
- }
256
- }
257
- }
258
-
259
- return listOf (pinAction)
260
- }
261
- }
262
18
263
19
/* *
264
20
* Controller class for managing the plan data and UI updates
@@ -293,9 +49,7 @@ class PlanController(
293
49
return
294
50
}
295
51
296
- // Save current states of all tasks
297
52
val taskStateMap = mutableMapOf<String , Pair <Boolean , TaskStatus >>()
298
-
299
53
agentTaskItems.forEach { planItem ->
300
54
planItem.steps.forEach { task ->
301
55
taskStateMap[task.step] = Pair (task.completed, task.status)
@@ -306,9 +60,7 @@ class PlanController(
306
60
307
61
newPlanItems.forEach { newItem ->
308
62
agentTaskItems.add(newItem)
309
-
310
63
newItem.steps.forEach { task ->
311
- // Restore saved states if available
312
64
taskStateMap[task.step]?.let { (completed, status) ->
313
65
task.completed = completed
314
66
task.status = status
0 commit comments