Skip to content

Commit ff8937b

Browse files
committed
feat(sketch): add plan compression and auto-pin to tool window #408
- Add compressed view mode with toggle functionality for plan display - Implement auto-pin feature to automatically open plans in tool window - Add dynamic task count display in compressed panel header - Enhance UI with expand/collapse controls and improved layout management
1 parent 03fcbce commit ff8937b

File tree

1 file changed

+153
-12
lines changed

1 file changed

+153
-12
lines changed

core/src/main/kotlin/cc/unitmesh/devti/sketch/ui/plan/PlanLangSketch.kt

Lines changed: 153 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,114 @@
11
package cc.unitmesh.devti.sketch.ui.plan
22

3+
import cc.unitmesh.devti.gui.AutoDevPlannerToolWindowFactory
4+
import cc.unitmesh.devti.gui.planner.AutoDevPlannerToolWindow
35
import cc.unitmesh.devti.observer.agent.AgentStateService
46
import cc.unitmesh.devti.observer.plan.AgentTaskEntry
57
import cc.unitmesh.devti.observer.plan.MarkdownPlanParser
68
import cc.unitmesh.devti.observer.plan.TaskStatus
79
import cc.unitmesh.devti.sketch.ui.ExtensionLangSketch
10+
import com.intellij.icons.AllIcons
811
import com.intellij.openapi.project.Project
12+
import com.intellij.openapi.wm.ToolWindowManager
13+
import com.intellij.ui.components.JBLabel
914
import com.intellij.ui.components.JBPanel
1015
import com.intellij.ui.components.JBScrollPane
1116
import com.intellij.ui.components.panels.VerticalLayout
1217
import com.intellij.util.ui.JBUI
1318
import java.awt.BorderLayout
19+
import java.awt.Cursor
1420
import java.awt.Dimension
21+
import java.awt.FlowLayout
22+
import java.awt.event.MouseAdapter
23+
import java.awt.event.MouseEvent
1524
import javax.swing.JComponent
25+
import javax.swing.JLabel
1626
import javax.swing.JPanel
1727
import javax.swing.ScrollPaneConstants
28+
import javax.swing.SwingUtilities.invokeLater
1829

1930
class PlanLangSketch(
2031
private val project: Project,
2132
private var content: String,
2233
private var agentTaskItems: MutableList<AgentTaskEntry>,
23-
private val isInToolwindow: Boolean = false
34+
private val isInToolwindow: Boolean = false,
35+
private val autoPinEnabled: Boolean = true
2436
) : JBPanel<PlanLangSketch>(BorderLayout(JBUI.scale(0), 0)), ExtensionLangSketch {
2537
private val contentPanel = JPanel(VerticalLayout(JBUI.scale(0)))
26-
val scrollPane: JBScrollPane
38+
var scrollPane: JBScrollPane? = null
2739
private val toolbarFactory = PlanToolbarFactory(project)
2840
private var hasUpdated = false
2941

42+
// 压缩模式相关
43+
private var isCompressed = false
44+
private var compressedPanel: JPanel? = null
45+
private var fullContentPanel: JPanel? = null
46+
3047
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()
3376
border = JBUI.Borders.compound(
3477
JBUI.Borders.empty(0, 4),
3578
JBUI.Borders.customLine(JBUI.CurrentTheme.ToolWindow.borderColor(), 1)
3679
)
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)
37103
}
104+
}
38105

39-
renderPlan()
106+
private fun setupFullContentPanel() {
107+
if (!isInToolwindow) {
108+
val toolbar = toolbarFactory.createToolbar(this)
109+
// 在工具栏中添加压缩按钮
110+
addCompressButtonToToolbar(toolbar)
111+
}
40112

41113
scrollPane = JBScrollPane(contentPanel).apply {
42114
verticalScrollBarPolicy = ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS
@@ -47,14 +119,48 @@ class PlanLangSketch(
47119
viewport.view = contentPanel
48120
}
49121

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()
56124

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+
}
58164
}
59165

60166
fun renderPlan() {
@@ -70,6 +176,19 @@ class PlanLangSketch(
70176

71177
contentPanel.revalidate()
72178
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+
}
73192
}
74193

75194
fun updatePlan(newPlanItems: List<AgentTaskEntry>) {
@@ -118,10 +237,32 @@ class PlanLangSketch(
118237
updatePlan(agentPlans)
119238
savePlanToService()
120239

240+
// 自动Pin到工具窗口
241+
if (autoPinEnabled) {
242+
autoPinToToolWindow()
243+
}
244+
121245
hasUpdated = true
122246
}
123247
}
124248

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+
125266
override fun getComponent(): JComponent = this
126267

127268
override fun dispose() {}

0 commit comments

Comments
 (0)