1
1
package cc.unitmesh.devti.sketch.ui.plan
2
2
3
+ import cc.unitmesh.devti.gui.AutoDevPlannerToolWindowFactory
4
+ import cc.unitmesh.devti.gui.planner.AutoDevPlannerToolWindow
3
5
import cc.unitmesh.devti.observer.agent.AgentStateService
4
6
import cc.unitmesh.devti.observer.plan.AgentTaskEntry
5
7
import cc.unitmesh.devti.observer.plan.MarkdownPlanParser
6
8
import cc.unitmesh.devti.observer.plan.TaskStatus
7
9
import cc.unitmesh.devti.sketch.ui.ExtensionLangSketch
10
+ import com.intellij.icons.AllIcons
8
11
import com.intellij.openapi.project.Project
12
+ import com.intellij.openapi.wm.ToolWindowManager
13
+ import com.intellij.ui.components.JBLabel
9
14
import com.intellij.ui.components.JBPanel
10
15
import com.intellij.ui.components.JBScrollPane
11
16
import com.intellij.ui.components.panels.VerticalLayout
12
17
import com.intellij.util.ui.JBUI
13
18
import java.awt.BorderLayout
19
+ import java.awt.Cursor
14
20
import java.awt.Dimension
21
+ import java.awt.FlowLayout
22
+ import java.awt.event.MouseAdapter
23
+ import java.awt.event.MouseEvent
15
24
import javax.swing.JComponent
25
+ import javax.swing.JLabel
16
26
import javax.swing.JPanel
17
27
import javax.swing.ScrollPaneConstants
28
+ import javax.swing.SwingUtilities.invokeLater
18
29
19
30
class PlanLangSketch (
20
31
private val project : Project ,
21
32
private var content : String ,
22
33
private var agentTaskItems : MutableList <AgentTaskEntry >,
23
- private val isInToolwindow : Boolean = false
34
+ private val isInToolwindow : Boolean = false ,
35
+ private val autoPinEnabled : Boolean = true
24
36
) : JBPanel<PlanLangSketch>(BorderLayout (JBUI .scale(0), 0)), ExtensionLangSketch {
25
37
private val contentPanel = JPanel (VerticalLayout (JBUI .scale(0 )))
26
- val scrollPane: JBScrollPane
38
+ var scrollPane: JBScrollPane ? = null
27
39
private val toolbarFactory = PlanToolbarFactory (project)
28
40
private var hasUpdated = false
29
41
42
+ // 压缩模式相关
43
+ private var isCompressed = false
44
+ private var compressedPanel: JPanel ? = null
45
+ private var fullContentPanel: JPanel ? = null
46
+
30
47
init {
31
- if (! isInToolwindow) {
32
- add(toolbarFactory.createToolbar(this ), BorderLayout .NORTH )
48
+ setupUI()
49
+ renderPlan()
50
+
51
+ // 默认Pin到工具窗口(如果启用)
52
+ if (autoPinEnabled && ! isInToolwindow) {
53
+ // 延迟执行,确保组件已完全初始化
54
+ invokeLater {
55
+ autoPinToToolWindow()
56
+ }
57
+ }
58
+ }
59
+
60
+ private fun setupUI () {
61
+ // 创建压缩面板
62
+ setupCompressedPanel()
63
+
64
+ // 创建完整内容面板
65
+ setupFullContentPanel()
66
+
67
+ // 默认显示完整内容
68
+ showFullContent()
69
+
70
+ minimumSize = Dimension (200 , 0 )
71
+ }
72
+
73
+ private fun setupCompressedPanel () {
74
+ compressedPanel = JPanel (BorderLayout ()).apply {
75
+ background = JBUI .CurrentTheme .ToolWindow .background()
33
76
border = JBUI .Borders .compound(
34
77
JBUI .Borders .empty(0 , 4 ),
35
78
JBUI .Borders .customLine(JBUI .CurrentTheme .ToolWindow .borderColor(), 1 )
36
79
)
80
+
81
+ val titlePanel = JPanel (FlowLayout (FlowLayout .LEFT , 5 , 5 )).apply {
82
+ background = JBUI .CurrentTheme .ToolWindow .background()
83
+
84
+ val expandIcon = JBLabel (AllIcons .General .ArrowRight ).apply {
85
+ cursor = Cursor .getPredefinedCursor(Cursor .HAND_CURSOR )
86
+ addMouseListener(object : MouseAdapter () {
87
+ override fun mouseClicked (e : MouseEvent ) {
88
+ toggleCompression()
89
+ }
90
+ })
91
+ }
92
+
93
+ val titleLabel = JLabel (" Plan (${agentTaskItems.size} tasks)" ).apply {
94
+ font = font.deriveFont(font.style or java.awt.Font .BOLD )
95
+ }
96
+
97
+ add(expandIcon)
98
+ add(titleLabel)
99
+ }
100
+
101
+ add(titlePanel, BorderLayout .CENTER )
102
+ preferredSize = Dimension (preferredSize.width, 30 )
37
103
}
104
+ }
38
105
39
- renderPlan()
106
+ private fun setupFullContentPanel () {
107
+ if (! isInToolwindow) {
108
+ val toolbar = toolbarFactory.createToolbar(this )
109
+ // 在工具栏中添加压缩按钮
110
+ addCompressButtonToToolbar(toolbar)
111
+ }
40
112
41
113
scrollPane = JBScrollPane (contentPanel).apply {
42
114
verticalScrollBarPolicy = ScrollPaneConstants .VERTICAL_SCROLLBAR_ALWAYS
@@ -47,14 +119,48 @@ class PlanLangSketch(
47
119
viewport.view = contentPanel
48
120
}
49
121
50
- val wrapperPanel = JPanel (BorderLayout ())
51
- wrapperPanel.add(scrollPane, BorderLayout .CENTER )
52
- wrapperPanel.background = JBUI .CurrentTheme .ToolWindow .background()
53
-
54
- // 将包装面板添加到当前面板
55
- add(wrapperPanel, BorderLayout .CENTER )
122
+ fullContentPanel = JPanel (BorderLayout ()).apply {
123
+ background = JBUI .CurrentTheme .ToolWindow .background()
56
124
57
- minimumSize = Dimension (200 , 0 )
125
+ if (! isInToolwindow) {
126
+ val toolbar = toolbarFactory.createToolbar(this @PlanLangSketch)
127
+ add(toolbar, BorderLayout .NORTH )
128
+ border = JBUI .Borders .compound(
129
+ JBUI .Borders .empty(0 , 4 ),
130
+ JBUI .Borders .customLine(JBUI .CurrentTheme .ToolWindow .borderColor(), 1 )
131
+ )
132
+ }
133
+
134
+ add(scrollPane, BorderLayout .CENTER )
135
+ }
136
+ }
137
+
138
+ private fun addCompressButtonToToolbar (toolbar : JComponent ) {
139
+ // 这里可以添加压缩按钮到工具栏,但为了简化,我们使用双击标题来切换
140
+ }
141
+
142
+ private fun showFullContent () {
143
+ removeAll()
144
+ fullContentPanel?.let { add(it, BorderLayout .CENTER ) }
145
+ isCompressed = false
146
+ revalidate()
147
+ repaint()
148
+ }
149
+
150
+ private fun showCompressedContent () {
151
+ removeAll()
152
+ compressedPanel?.let { add(it, BorderLayout .CENTER ) }
153
+ isCompressed = true
154
+ revalidate()
155
+ repaint()
156
+ }
157
+
158
+ fun toggleCompression () {
159
+ if (isCompressed) {
160
+ showFullContent()
161
+ } else {
162
+ showCompressedContent()
163
+ }
58
164
}
59
165
60
166
fun renderPlan () {
@@ -70,6 +176,19 @@ class PlanLangSketch(
70
176
71
177
contentPanel.revalidate()
72
178
contentPanel.repaint()
179
+
180
+ // 更新压缩面板中的任务数量
181
+ updateCompressedPanelInfo()
182
+ }
183
+
184
+ private fun updateCompressedPanelInfo () {
185
+ compressedPanel?.let { panel ->
186
+ val titlePanel = panel.getComponent(0 ) as ? JPanel
187
+ titlePanel?.let { tp ->
188
+ val titleLabel = tp.components.find { it is JLabel && it != tp.components[0 ] } as ? JLabel
189
+ titleLabel?.text = " Plan (${agentTaskItems.size} tasks)"
190
+ }
191
+ }
73
192
}
74
193
75
194
fun updatePlan (newPlanItems : List <AgentTaskEntry >) {
@@ -118,10 +237,32 @@ class PlanLangSketch(
118
237
updatePlan(agentPlans)
119
238
savePlanToService()
120
239
240
+ // 自动Pin到工具窗口
241
+ if (autoPinEnabled) {
242
+ autoPinToToolWindow()
243
+ }
244
+
121
245
hasUpdated = true
122
246
}
123
247
}
124
248
249
+ private fun autoPinToToolWindow () {
250
+ val toolWindowManager = ToolWindowManager .getInstance(project)
251
+ val toolWindow = toolWindowManager.getToolWindow(AutoDevPlannerToolWindowFactory .PlANNER_ID )
252
+ ? : return
253
+
254
+ val codingPanel = toolWindow.contentManager.component.components?.filterIsInstance<AutoDevPlannerToolWindow >()
255
+ ?.firstOrNull()
256
+
257
+ toolWindow.activate {
258
+ val agentStateService = project.getService(AgentStateService ::class .java)
259
+ val currentPlan = agentStateService.getPlan()
260
+ val planString = MarkdownPlanParser .formatPlanToMarkdown(currentPlan)
261
+
262
+ codingPanel?.switchToPlanView(planString)
263
+ }
264
+ }
265
+
125
266
override fun getComponent (): JComponent = this
126
267
127
268
override fun dispose () {}
0 commit comments