@@ -4,19 +4,21 @@ import com.ashotn.opencode.relay.OpenCodePlugin
44import com.ashotn.opencode.relay.OpenCodeProcessEnvironment
55import com.ashotn.opencode.relay.settings.OpenCodeSettings
66import com.ashotn.opencode.relay.util.serverUrl
7+ import com.intellij.ide.DataManager
78import com.intellij.openapi.Disposable
9+ import com.intellij.openapi.actionSystem.CustomizedDataContext
10+ import com.intellij.openapi.actionSystem.PlatformDataKeys
811import com.intellij.openapi.application.ApplicationManager
912import com.intellij.openapi.diagnostic.Logger
1013import com.intellij.openapi.project.Project
1114import com.intellij.openapi.ui.ComponentContainer
1215import com.intellij.openapi.util.Disposer
16+ import com.intellij.terminal.JBTerminalPanel
1317import com.intellij.terminal.ui.TerminalWidget
1418import org.jetbrains.plugins.terminal.LocalTerminalDirectRunner
1519import org.jetbrains.plugins.terminal.ShellStartupOptions
1620import org.jetbrains.plugins.terminal.ShellTerminalWidget
1721import java.awt.BorderLayout
18- import java.awt.event.KeyEvent
19- import java.awt.event.KeyListener
2022import java.lang.reflect.Proxy
2123import javax.swing.JPanel
2224
@@ -37,6 +39,7 @@ class ClassicTuiPanel(
3739) : JPanel(BorderLayout ()), TuiPanel, Disposable {
3840
3941 private var terminalWidget: TerminalWidget ? = null
42+ private var terminalPanel: JBTerminalPanel ? = null
4043
4144 init {
4245 Disposer .register(parentDisposable, this )
@@ -84,12 +87,16 @@ class ClassicTuiPanel(
8487 val widget = runner.startShellTerminalWidget(this , startupOptions, true )
8588 terminalWidget = widget
8689 Disposer .register(this , widget)
90+ terminalPanel = ShellTerminalWidget .asShellJediTermWidget(widget)?.terminalPanel?.also { panel ->
91+ installEmbeddedTerminalDataProvider(panel)
92+ }
8793
8894 // When the shell process exits, clean up and notify the owner.
8995 widget.addTerminationCallback({
9096 ApplicationManager .getApplication().invokeLater {
9197 logger.debug(" Classic terminal process terminated" )
9298 if (terminalWidget == = widget) {
99+ uninstallEmbeddedTerminalDataProvider()
93100 terminalWidget = null
94101 remove(widget.component)
95102 revalidate()
@@ -101,19 +108,6 @@ class ClassicTuiPanel(
101108
102109 add(widget.component, BorderLayout .CENTER )
103110
104- // Consume ESC key to prevent IDE from stealing focus from the inline terminal.
105- widget.component.addKeyListener(object : KeyListener {
106- override fun keyPressed (e : KeyEvent ) {
107- if (e.keyCode == KeyEvent .VK_ESCAPE ) {
108- e.consume()
109- }
110- }
111-
112- override fun keyTyped (e : KeyEvent ) {}
113-
114- override fun keyReleased (e : KeyEvent ) {}
115- })
116-
117111 revalidate()
118112 repaint()
119113
@@ -127,7 +121,7 @@ class ClassicTuiPanel(
127121 }
128122
129123 override fun focusTerminal () {
130- terminalWidget?.component ?.requestFocusInWindow()
124+ terminalWidget?.preferredFocusableComponent ?.requestFocusInWindow()
131125 }
132126
133127 override val isStarted: Boolean get() = terminalWidget != null
@@ -136,6 +130,7 @@ class ClassicTuiPanel(
136130
137131 private fun tearDown () {
138132 val widget = terminalWidget ? : return
133+ uninstallEmbeddedTerminalDataProvider()
139134 terminalWidget = null
140135 remove(widget.component)
141136 revalidate()
@@ -150,6 +145,24 @@ class ClassicTuiPanel(
150145
151146 override fun dispose () = tearDown()
152147
148+ private fun installEmbeddedTerminalDataProvider (panel : JBTerminalPanel ) {
149+ // JBTerminalPanel's internal TerminalEscapeKeyListener moves focus back to the editor
150+ // whenever the terminal reports a ToolWindow in its data context. Our terminal is embedded
151+ // inside a custom tool window, so return an explicit null for TOOL_WINDOW to keep ESC inside
152+ // the TUI while still allowing the key event to reach the terminal process.
153+ DataManager .registerDataProvider(panel) { dataId ->
154+ when {
155+ PlatformDataKeys .TOOL_WINDOW .`is `(dataId) -> CustomizedDataContext .EXPLICIT_NULL
156+ else -> null
157+ }
158+ }
159+ }
160+
161+ private fun uninstallEmbeddedTerminalDataProvider () {
162+ terminalPanel?.let (DataManager ::removeDataProvider)
163+ terminalPanel = null
164+ }
165+
153166 companion object {
154167 private val logger = Logger .getInstance(ClassicTuiPanel ::class .java)
155168
0 commit comments