Skip to content

Commit c35028e

Browse files
committed
refactor(plan): split PlanSketch into separate files #331
Extract TaskPanel, SectionPanel, and PlanToolbarFactory into their own files for better maintainability and organization
1 parent 6e2da4a commit c35028e

File tree

4 files changed

+279
-248
lines changed

4 files changed

+279
-248
lines changed

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

Lines changed: 0 additions & 248 deletions
Original file line numberDiff line numberDiff line change
@@ -1,264 +1,20 @@
11
package cc.unitmesh.devti.sketch.ui.plan
22

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
73
import cc.unitmesh.devti.observer.agent.AgentStateService
8-
import cc.unitmesh.devti.observer.plan.AgentPlanStep
94
import cc.unitmesh.devti.observer.plan.AgentTaskEntry
105
import cc.unitmesh.devti.observer.plan.MarkdownPlanParser
116
import cc.unitmesh.devti.observer.plan.TaskStatus
127
import cc.unitmesh.devti.sketch.ui.ExtensionLangSketch
13-
import com.intellij.icons.AllIcons
148
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
199
import com.intellij.openapi.project.Project
20-
import com.intellij.openapi.wm.ToolWindowManager
21-
import com.intellij.ui.components.JBCheckBox
2210
import com.intellij.ui.components.JBPanel
23-
import com.intellij.ui.components.panels.Wrapper
2411
import com.intellij.util.ui.JBEmptyBorder
2512
import com.intellij.util.ui.JBUI
26-
import com.intellij.util.ui.UIUtil
2713
import java.awt.BorderLayout
28-
import java.awt.Dimension
29-
import java.awt.FlowLayout
30-
import javax.swing.BorderFactory
3114
import javax.swing.Box
3215
import javax.swing.BoxLayout
33-
import javax.swing.JButton
3416
import javax.swing.JComponent
35-
import javax.swing.JLabel
36-
import javax.swing.JMenuItem
3717
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-
}
26218

26319
/**
26420
* Controller class for managing the plan data and UI updates
@@ -293,9 +49,7 @@ class PlanController(
29349
return
29450
}
29551

296-
// Save current states of all tasks
29752
val taskStateMap = mutableMapOf<String, Pair<Boolean, TaskStatus>>()
298-
29953
agentTaskItems.forEach { planItem ->
30054
planItem.steps.forEach { task ->
30155
taskStateMap[task.step] = Pair(task.completed, task.status)
@@ -306,9 +60,7 @@ class PlanController(
30660

30761
newPlanItems.forEach { newItem ->
30862
agentTaskItems.add(newItem)
309-
31063
newItem.steps.forEach { task ->
311-
// Restore saved states if available
31264
taskStateMap[task.step]?.let { (completed, status) ->
31365
task.completed = completed
31466
task.status = status
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
package cc.unitmesh.devti.sketch.ui.plan
2+
3+
import cc.unitmesh.devti.gui.AutoDevPlanerToolWindowFactory
4+
import com.intellij.icons.AllIcons
5+
import com.intellij.openapi.actionSystem.ActionManager
6+
import com.intellij.openapi.actionSystem.AnAction
7+
import com.intellij.openapi.actionSystem.AnActionEvent
8+
import com.intellij.openapi.actionSystem.DefaultActionGroup
9+
import com.intellij.openapi.project.Project
10+
import com.intellij.openapi.wm.ToolWindowManager
11+
import com.intellij.ui.components.panels.Wrapper
12+
import com.intellij.util.ui.JBUI
13+
import com.intellij.util.ui.UIUtil
14+
import java.awt.BorderLayout
15+
import javax.swing.JComponent
16+
import javax.swing.JLabel
17+
import javax.swing.JPanel
18+
19+
/**
20+
* Toolbar factory for creating the plan sketch toolbar
21+
*/
22+
class PlanToolbarFactory(private val project: Project) {
23+
fun createToolbar(): JComponent {
24+
val actionGroup = DefaultActionGroup(createToolbarActions())
25+
val toolbar = ActionManager.getInstance()
26+
.createActionToolbar("PlanSketch", actionGroup, true)
27+
28+
val titleLabel = JLabel("Thought Plan").apply {
29+
border = JBUI.Borders.empty(0, 10)
30+
}
31+
32+
val toolbarPanel = JPanel(BorderLayout()).apply {
33+
add(titleLabel, BorderLayout.WEST)
34+
add(toolbar.component, BorderLayout.EAST)
35+
}
36+
37+
val toolbarWrapper = Wrapper(JBUI.Panels.simplePanel(toolbarPanel)).also {
38+
it.border = JBUI.Borders.customLine(UIUtil.getBoundsColor(), 1, 1, 1, 1)
39+
}
40+
41+
return toolbarWrapper
42+
}
43+
44+
private fun createToolbarActions(): List<AnAction> {
45+
val pinAction = object : AnAction("Pin", "Show in popup window", AllIcons.Toolbar.Pin) {
46+
override fun displayTextInToolbar(): Boolean = true
47+
48+
override fun actionPerformed(e: AnActionEvent) {
49+
val toolWindow =
50+
ToolWindowManager.Companion.getInstance(project).getToolWindow(AutoDevPlanerToolWindowFactory.Companion.PlANNER_ID)
51+
?: return
52+
53+
toolWindow.activate {
54+
// todo
55+
}
56+
}
57+
}
58+
59+
return listOf(pinAction)
60+
}
61+
}

0 commit comments

Comments
 (0)