Skip to content

Commit 92e3b15

Browse files
committed
feat(terminal): improve collapsible panel and add execution result feedback && closed #335
- Add border to collapsible panel title - Remove border from collapsible panel content- Implement result status visualization (success/error) - Add show/hide code panel functionality - Update UI to reflect execution status
1 parent a1b4dd8 commit 92e3b15

File tree

2 files changed

+67
-10
lines changed

2 files changed

+67
-10
lines changed

exts/ext-terminal/src/main/kotlin/cc/unitmesh/terminal/sketch/CollapsiblePanel.kt

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,13 @@ class CollapsiblePanel(title: String, private val contentPanel: JPanel, initiall
1515

1616
private var isCollapsed = initiallyCollapsed
1717
private val headerPanel = JBPanel<JBPanel<*>>(BorderLayout())
18-
val titleLabel = JBLabel(title)
18+
val titleLabel = JBLabel(title).apply {
19+
border = JBUI.Borders.empty(4)
20+
}
21+
1922
private val toggleLabel = JBLabel()
2023

2124
init {
22-
headerPanel.border = JBUI.Borders.empty(5)
2325
headerPanel.add(titleLabel, BorderLayout.CENTER)
2426
headerPanel.add(toggleLabel, BorderLayout.EAST)
2527
headerPanel.cursor = Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)
@@ -36,6 +38,7 @@ class CollapsiblePanel(title: String, private val contentPanel: JPanel, initiall
3638
add(contentPanel, BorderLayout.CENTER)
3739

3840
contentPanel.isVisible = !isCollapsed
41+
border = JBUI.Borders.empty()
3942
}
4043

4144
private fun updateToggleIcon() {

exts/ext-terminal/src/main/kotlin/cc/unitmesh/terminal/sketch/TerminalSketchProvider.kt

Lines changed: 62 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ import javax.swing.JComponent
5050
import javax.swing.JLabel
5151
import javax.swing.JPanel
5252
import javax.swing.SwingConstants
53+
import javax.swing.border.LineBorder
5354

5455
class TerminalSketchProvider : LanguageSketchProvider {
5556
override fun isSupported(lang: String): Boolean = lang == "bash" || lang == "shell"
@@ -83,11 +84,15 @@ class TerminalLangSketch(val project: Project, var content: String) : ExtensionL
8384
val resultSketch = CodeHighlightSketch(project, "", CodeFence.findLanguage("bash")).apply {
8485
border = JBUI.Borders.empty()
8586
}
87+
8688
val resultPanel = JPanel(BorderLayout()).apply {
89+
border = JBUI.Borders.empty()
8790
add(resultSketch.getComponent(), BorderLayout.CENTER)
8891
}
8992

90-
val collapsibleCodePanel = CollapsiblePanel("Shell Code", codePanel, initiallyCollapsed = true)
93+
private val successColor = JBColor(Color(233, 255, 233), Color(0, 77, 0))
94+
private val errorColor = JBColor(Color(255, 233, 233), Color(77, 0, 0))
95+
private val normalColor = resultPanel.background
9196

9297
val collapsibleResultPanel = CollapsiblePanel("Execution Results", resultPanel, initiallyCollapsed = true)
9398

@@ -101,7 +106,8 @@ class TerminalLangSketch(val project: Project, var content: String) : ExtensionL
101106
}
102107

103108
private lateinit var executeAction: TerminalExecuteAction
104-
private lateinit var resizableTerminalPanel: ResizableTerminalPanel
109+
private var resizableTerminalPanel: ResizableTerminalPanel
110+
private var isCodePanelVisible = false
105111

106112
init {
107113
val projectDir = project.guessProjectDir()?.path
@@ -119,22 +125,65 @@ class TerminalLangSketch(val project: Project, var content: String) : ExtensionL
119125
mainPanel = object : JPanel(VerticalLayout(JBUI.scale(0))) {
120126
init {
121127
add(toolbarWrapper)
122-
add(collapsibleCodePanel)
123128
add(collapsibleResultPanel)
124129
add(resizableTerminalPanel)
125130
}
126131
}
127132

128133
mainPanel!!.border = JBUI.Borders.compound(
129134
JBUI.Borders.customLine(UIUtil.getBoundsColor(), 1),
130-
JBUI.Borders.empty(0, 4)
135+
JBUI.Borders.empty()
131136
)
132137
terminalWidget!!.addMessageFilter(FrontendWebViewServerFilter(project, mainPanel!!))
133138
}
139+
140+
private fun setResultStatus(success: Boolean, errorMessage: String? = null) {
141+
ApplicationManager.getApplication().invokeLater {
142+
when {
143+
success -> {
144+
resultPanel.background = successColor
145+
resultPanel.border = LineBorder(JBColor(Color(0, 128, 0), Color(0, 100, 0)), 1)
146+
collapsibleResultPanel.setTitle("✅ Execution Successful")
147+
}
148+
errorMessage != null -> {
149+
resultPanel.background = errorColor
150+
resultPanel.border = LineBorder(JBColor(Color(128, 0, 0), Color(100, 0, 0)), 1)
151+
collapsibleResultPanel.setTitle("❌ Execution Failed")
152+
}
153+
else -> {
154+
resultPanel.background = normalColor
155+
resultPanel.border = null
156+
collapsibleResultPanel.setTitle("Execution Results")
157+
}
158+
}
159+
resultPanel.repaint()
160+
}
161+
}
162+
163+
private fun toggleCodePanel() {
164+
if (isCodePanelVisible) {
165+
mainPanel!!.remove(codePanel)
166+
isCodePanelVisible = false
167+
} else {
168+
// Add code panel at index 1 (after toolbar, before result panel)
169+
mainPanel!!.add(codePanel, 1)
170+
isCodePanelVisible = true
171+
}
172+
173+
mainPanel!!.revalidate()
174+
mainPanel!!.repaint()
175+
}
134176

135177
fun createConsoleActions(): List<AnAction> {
136178
executeAction = TerminalExecuteAction()
137179

180+
val showCodeAction = object :
181+
AnAction("Show/Hide Code", "Show or hide the shell code", AllIcons.Actions.ShowCode) {
182+
override fun actionPerformed(e: AnActionEvent) {
183+
toggleCodePanel()
184+
}
185+
}
186+
138187
val copyAction = object :
139188
AnAction("Copy", AutoDevBundle.message("sketch.terminal.copy.text"), AllIcons.Actions.Copy) {
140189
override fun actionPerformed(e: AnActionEvent) {
@@ -170,7 +219,7 @@ class TerminalLangSketch(val project: Project, var content: String) : ExtensionL
170219
}
171220
}
172221

173-
return listOf(executeAction, copyAction, sendAction, popupAction)
222+
return listOf(executeAction, showCodeAction, copyAction, sendAction, popupAction)
174223
}
175224

176225
private fun executePopup(terminalWidget: JBTerminalWidget?, project: Project): MouseAdapter =
@@ -232,7 +281,7 @@ class TerminalLangSketch(val project: Project, var content: String) : ExtensionL
232281
"⚠️ WARNING: $reason\nThe command was not auto-executed for safety reasons.\nPlease review and run manually if you're sure.",
233282
true
234283
)
235-
collapsibleResultPanel.setTitle("Safety Warning")
284+
setResultStatus(false, reason)
236285
collapsibleResultPanel.expand()
237286
}
238287
return
@@ -286,23 +335,28 @@ class TerminalLangSketch(val project: Project, var content: String) : ExtensionL
286335

287336
resultSketch.updateViewText("", true)
288337
stdWriter.setExecuting(true)
338+
// Reset result panel appearance
339+
setResultStatus(false)
289340

290341
AutoDevCoroutineScope.scope(project).launch {
291342
val executor = ProcessExecutor(project)
292343
try {
293344
val dispatcher = PooledThreadExecutor.INSTANCE.asCoroutineDispatcher()
294-
executor.exec(getViewText(), stdWriter, stdWriter, dispatcher)
345+
val exitCode = executor.exec(getViewText(), stdWriter, stdWriter, dispatcher)
295346
ApplicationManager.getApplication().invokeLater {
296347
stdWriter.setExecuting(false)
297348
if (collapsibleResultPanel.isCollapsed()) {
298349
collapsibleResultPanel.expand()
299350
}
351+
// Set success/failure based on exit code
352+
val success = exitCode == 0
353+
setResultStatus(success, if (!success) "Process exited with code $exitCode" else null)
300354
}
301355
} catch (ex: Exception) {
302356
ApplicationManager.getApplication().invokeLater {
303357
stdWriter.setExecuting(false)
304358
resultSketch.updateViewText("${stdWriter.getContent()}\nError: ${ex.message}", true)
305-
collapsibleResultPanel.setTitle("Execution Results (Error)")
359+
setResultStatus(false, ex.message)
306360
}
307361
}
308362
}

0 commit comments

Comments
 (0)