v1.3.5: add Send button + session limit + fix compile order
This commit is contained in:
@@ -64,6 +64,21 @@ fun SshHost(modifier: Modifier = Modifier) {
|
||||
var searchQuery by remember { mutableStateOf("") }
|
||||
val coroutineScope = rememberCoroutineScope()
|
||||
|
||||
val MAX_SESSIONS = 5
|
||||
|
||||
fun closeSession(index: Int) {
|
||||
if (index in activeSessions.indices) {
|
||||
activeSessions[index].disconnect()
|
||||
activeSessions.removeAt(index)
|
||||
selectedTabIndex = when {
|
||||
activeSessions.isEmpty() -> -1
|
||||
selectedTabIndex >= activeSessions.size -> activeSessions.size - 1
|
||||
selectedTabIndex > index -> selectedTabIndex - 1
|
||||
else -> selectedTabIndex
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun openSession(config: SshConfig) {
|
||||
// Bug 3: 如果密码为空,弹出连接对话框让用户输入密码
|
||||
if (config.password.isBlank() && config.authMode != "key") {
|
||||
@@ -71,6 +86,10 @@ fun SshHost(modifier: Modifier = Modifier) {
|
||||
showConnectDialog = true
|
||||
return
|
||||
}
|
||||
// 限制最大会话数:超过上限时关掉最旧的
|
||||
if (activeSessions.size >= MAX_SESSIONS) {
|
||||
closeSession(0)
|
||||
}
|
||||
// 立即创建 tab 显示"连接中"
|
||||
val mgr = SshTerminalManager(sessionId = "${config.host}:${config.port}")
|
||||
activeSessions.add(mgr)
|
||||
@@ -89,19 +108,6 @@ fun SshHost(modifier: Modifier = Modifier) {
|
||||
}
|
||||
}
|
||||
|
||||
fun closeSession(index: Int) {
|
||||
if (index in activeSessions.indices) {
|
||||
activeSessions[index].disconnect()
|
||||
activeSessions.removeAt(index)
|
||||
selectedTabIndex = when {
|
||||
activeSessions.isEmpty() -> -1
|
||||
selectedTabIndex >= activeSessions.size -> activeSessions.size - 1
|
||||
selectedTabIndex > index -> selectedTabIndex - 1
|
||||
else -> selectedTabIndex
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val keyFilePickerLauncher = rememberLauncherForActivityResult(
|
||||
contract = ActivityResultContracts.OpenDocument()
|
||||
) { uri: Uri? ->
|
||||
|
||||
@@ -16,6 +16,7 @@ import android.view.KeyEvent as AndroidKeyEvent
|
||||
import android.view.inputmethod.EditorInfo
|
||||
import androidx.compose.ui.viewinterop.AndroidView
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.filled.Send
|
||||
import androidx.compose.material.icons.filled.CheckCircle
|
||||
import androidx.compose.material.icons.filled.Close
|
||||
import androidx.compose.material.icons.filled.Folder
|
||||
@@ -519,7 +520,7 @@ fun SshTerminalScreen(
|
||||
setBackgroundColor(android.graphics.Color.TRANSPARENT)
|
||||
setTextColor(android.graphics.Color.parseColor("#E6EDF3"))
|
||||
textSize = 14f
|
||||
hint = if (state.connected) "type command..." else ""
|
||||
hint = if (state.connected) "输入命令..." else ""
|
||||
setHintTextColor(android.graphics.Color.parseColor("#6E7681"))
|
||||
imeOptions = EditorInfo.IME_ACTION_SEND
|
||||
inputType = android.text.InputType.TYPE_CLASS_TEXT
|
||||
@@ -528,52 +529,30 @@ fun SshTerminalScreen(
|
||||
if (actionId == EditorInfo.IME_ACTION_SEND || actionId == EditorInfo.IME_ACTION_GO || actionId == EditorInfo.IME_ACTION_DONE) {
|
||||
val cmd = text.toString().trim()
|
||||
if (cmd.isNotEmpty()) {
|
||||
coroutineScope.launch {
|
||||
manager.sendCommand(cmd)
|
||||
}
|
||||
coroutineScope.launch { manager.sendCommand(cmd) }
|
||||
}
|
||||
setText("")
|
||||
currentInput = ""
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
} else false
|
||||
}
|
||||
addTextChangedListener(object : android.text.TextWatcher {
|
||||
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
|
||||
override fun afterTextChanged(s: android.text.Editable?) {}
|
||||
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
|
||||
val text = s?.toString() ?: ""
|
||||
// 检测回车字符:输入法按回车时会在文本末尾插入 \n
|
||||
if (text.endsWith('\n')) {
|
||||
val cmd = text.dropLast(1).trim()
|
||||
val t = s?.toString() ?: ""
|
||||
if (t.endsWith('\n')) {
|
||||
val cmd = t.dropLast(1).trim()
|
||||
if (cmd.isNotEmpty()) {
|
||||
coroutineScope.launch {
|
||||
manager.sendCommand(cmd)
|
||||
}
|
||||
coroutineScope.launch { manager.sendCommand(cmd) }
|
||||
}
|
||||
setText("")
|
||||
currentInput = ""
|
||||
} else {
|
||||
currentInput = text
|
||||
currentInput = t
|
||||
}
|
||||
}
|
||||
})
|
||||
setOnKeyListener { _, keyCode, event ->
|
||||
if (event.action == android.view.KeyEvent.ACTION_DOWN && keyCode == AndroidKeyEvent.KEYCODE_ENTER) {
|
||||
val cmd = text.toString().trim()
|
||||
if (cmd.isNotEmpty()) {
|
||||
coroutineScope.launch {
|
||||
manager.sendCommand(cmd)
|
||||
}
|
||||
}
|
||||
setText("")
|
||||
currentInput = ""
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
tag = "terminal_input"
|
||||
editTextRef.value = this
|
||||
}
|
||||
@@ -582,6 +561,28 @@ fun SshTerminalScreen(
|
||||
.weight(1f)
|
||||
.fillMaxHeight()
|
||||
)
|
||||
|
||||
// 发送按钮
|
||||
if (state.connected) {
|
||||
IconButton(
|
||||
onClick = {
|
||||
val cmd = editTextRef.value?.text?.toString()?.trim() ?: ""
|
||||
if (cmd.isNotEmpty()) {
|
||||
coroutineScope.launch { manager.sendCommand(cmd) }
|
||||
editTextRef.value?.setText("")
|
||||
currentInput = ""
|
||||
}
|
||||
},
|
||||
modifier = Modifier.size(36.dp)
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.AutoMirrored.Filled.Send,
|
||||
contentDescription = "发送",
|
||||
tint = accentBlue,
|
||||
modifier = Modifier.size(18.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ── 终端内容(顶栏 + 输出 + 输入行)──
|
||||
|
||||
Reference in New Issue
Block a user