v1.3.5: add Send button + session limit + fix compile order

This commit is contained in:
茂之钳
2026-05-26 13:49:55 +00:00
parent 99f19faafa
commit b9d5ab2fea
2 changed files with 50 additions and 43 deletions
@@ -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)
)
}
}
}
// ── 终端内容(顶栏 + 输出 + 输入行)──